aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c')
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c577
1 files changed, 479 insertions, 98 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 97dc464379d2..3a92c09d4692 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -184,6 +184,29 @@ static const struct iwl_fw_bcast_filter iwl_mvm_default_bcast_filters[] = {
};
#endif
+static const struct cfg80211_pmsr_capabilities iwl_mvm_pmsr_capa = {
+ .max_peers = IWL_MVM_TOF_MAX_APS,
+ .report_ap_tsf = 1,
+ .randomize_mac_addr = 1,
+
+ .ftm = {
+ .supported = 1,
+ .asap = 1,
+ .non_asap = 1,
+ .request_lci = 1,
+ .request_civicloc = 1,
+ .max_bursts_exponent = -1, /* all supported */
+ .max_ftms_per_burst = 0, /* no limits */
+ .bandwidths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40) |
+ BIT(NL80211_CHAN_WIDTH_80),
+ .preambles = BIT(NL80211_PREAMBLE_LEGACY) |
+ BIT(NL80211_PREAMBLE_HT) |
+ BIT(NL80211_PREAMBLE_VHT),
+ },
+};
+
void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
{
if (!iwl_mvm_is_d0i3_supported(mvm))
@@ -395,6 +418,21 @@ int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)
return ret;
}
+const static u8 he_if_types_ext_capa_sta[] = {
+ [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
+ [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF,
+ [9] = WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT,
+};
+
+const static struct wiphy_iftype_ext_capab he_iftypes_ext_capa[] = {
+ {
+ .iftype = NL80211_IFTYPE_STATION,
+ .extended_capabilities = he_if_types_ext_capa_sta,
+ .extended_capabilities_mask = he_if_types_ext_capa_sta,
+ .extended_capabilities_len = sizeof(he_if_types_ext_capa_sta),
+ },
+};
+
int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
{
struct ieee80211_hw *hw = mvm->hw;
@@ -405,12 +443,15 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP,
};
+#ifdef CONFIG_PM_SLEEP
+ bool unified = fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG);
+#endif
/* Tell mac80211 our characteristics */
ieee80211_hw_set(hw, SIGNAL_DBM);
ieee80211_hw_set(hw, SPECTRUM_MGMT);
ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
- ieee80211_hw_set(hw, QUEUE_CONTROL);
ieee80211_hw_set(hw, WANT_MONITOR_VIF);
ieee80211_hw_set(hw, SUPPORTS_PS);
ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
@@ -424,6 +465,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR);
ieee80211_hw_set(hw, DEAUTH_NEED_MGD_TX_PREP);
ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
+ ieee80211_hw_set(hw, BUFF_MMPDU_TXQ);
+ ieee80211_hw_set(hw, STA_MMPDU_TXQ);
+ ieee80211_hw_set(hw, TX_AMSDU);
+ ieee80211_hw_set(hw, TX_FRAG_LIST);
if (iwl_mvm_has_tlc_offload(mvm)) {
ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
@@ -469,6 +514,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
+ hw->max_tx_fragments = mvm->trans->max_skb_frags;
BUILD_BUG_ON(ARRAY_SIZE(mvm->ciphers) < ARRAY_SIZE(mvm_ciphers) + 6);
memcpy(mvm->ciphers, mvm_ciphers, sizeof(mvm_ciphers));
@@ -525,6 +571,13 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->n_cipher_suites++;
}
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_FTM_CALIBRATED)) {
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER);
+ hw->wiphy->pmsr_capa = &iwl_mvm_pmsr_capa;
+ }
+
ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
hw->wiphy->features |=
NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
@@ -534,6 +587,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->sta_data_size = sizeof(struct iwl_mvm_sta);
hw->vif_data_size = sizeof(struct iwl_mvm_vif);
hw->chanctx_data_size = sizeof(u16);
+ hw->txq_data_size = sizeof(struct iwl_mvm_txq);
hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_P2P_CLIENT) |
@@ -673,6 +727,13 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE);
}
+ if (mvm->nvm_data->sku_cap_11ax_enable &&
+ !iwlwifi_mod_params.disable_11ax) {
+ hw->wiphy->iftype_ext_capab = he_iftypes_ext_capa;
+ hw->wiphy->num_iftype_ext_capab =
+ ARRAY_SIZE(he_iftypes_ext_capa);
+ }
+
mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
#ifdef CONFIG_PM_SLEEP
@@ -682,7 +743,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->wowlan = &mvm->wowlan;
}
- if (mvm->fw->img[IWL_UCODE_WOWLAN].num_sec &&
+ if ((unified || mvm->fw->img[IWL_UCODE_WOWLAN].num_sec) &&
mvm->trans->ops->d3_suspend &&
mvm->trans->ops->d3_resume &&
device_can_wakeup(mvm->trans->dev)) {
@@ -735,15 +796,15 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->netdev_features |= IWL_TX_CSUM_NETIF_FLAGS;
}
- ret = ieee80211_register_hw(mvm->hw);
- if (ret)
- iwl_mvm_leds_exit(mvm);
- mvm->init_status |= IWL_MVM_INIT_STATUS_REG_HW_INIT_COMPLETE;
-
if (mvm->cfg->vht_mu_mimo_supported)
wiphy_ext_feature_set(hw->wiphy,
NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER);
+ ret = ieee80211_register_hw(mvm->hw);
+ if (ret) {
+ iwl_mvm_leds_exit(mvm);
+ }
+
return ret;
}
@@ -776,7 +837,6 @@ static bool iwl_mvm_defer_tx(struct iwl_mvm *mvm,
goto out;
__skb_queue_tail(&mvm->d0i3_tx, skb);
- ieee80211_stop_queues(mvm->hw);
/* trigger wakeup */
iwl_mvm_ref(mvm, IWL_MVM_REF_TX);
@@ -796,13 +856,15 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
struct ieee80211_sta *sta = control->sta;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (void *)skb->data;
+ bool offchannel = IEEE80211_SKB_CB(skb)->flags &
+ IEEE80211_TX_CTL_TX_OFFCHAN;
if (iwl_mvm_is_radio_killed(mvm)) {
IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n");
goto drop;
}
- if (info->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE &&
+ if (offchannel &&
!test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status) &&
!test_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status))
goto drop;
@@ -815,8 +877,8 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
sta = NULL;
/* If there is no sta, and it's not offchannel - send through AP */
- if (info->control.vif->type == NL80211_IFTYPE_STATION &&
- info->hw_queue != IWL_MVM_OFFCHANNEL_QUEUE && !sta) {
+ if (!sta && info->control.vif->type == NL80211_IFTYPE_STATION &&
+ !offchannel) {
struct iwl_mvm_vif *mvmvif =
iwl_mvm_vif_from_mac80211(info->control.vif);
u8 ap_sta_id = READ_ONCE(mvmvif->ap_sta_id);
@@ -844,22 +906,101 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
ieee80211_free_txskb(hw, skb);
}
-static inline bool iwl_enable_rx_ampdu(const struct iwl_cfg *cfg)
+void iwl_mvm_mac_itxq_xmit(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
{
- if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG)
- return false;
- return true;
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_txq *mvmtxq = iwl_mvm_txq_from_mac80211(txq);
+ struct sk_buff *skb = NULL;
+
+ /*
+ * No need for threads to be pending here, they can leave the first
+ * taker all the work.
+ *
+ * mvmtxq->tx_request logic:
+ *
+ * If 0, no one is currently TXing, set to 1 to indicate current thread
+ * will now start TX and other threads should quit.
+ *
+ * If 1, another thread is currently TXing, set to 2 to indicate to
+ * that thread that there was another request. Since that request may
+ * have raced with the check whether the queue is empty, the TXing
+ * thread should check the queue's status one more time before leaving.
+ * This check is done in order to not leave any TX hanging in the queue
+ * until the next TX invocation (which may not even happen).
+ *
+ * If 2, another thread is currently TXing, and it will already double
+ * check the queue, so do nothing.
+ */
+ if (atomic_fetch_add_unless(&mvmtxq->tx_request, 1, 2))
+ return;
+
+ rcu_read_lock();
+ do {
+ while (likely(!mvmtxq->stopped &&
+ (mvm->trans->system_pm_mode ==
+ IWL_PLAT_PM_MODE_DISABLED))) {
+ skb = ieee80211_tx_dequeue(hw, txq);
+
+ if (!skb) {
+ if (txq->sta)
+ IWL_DEBUG_TX(mvm,
+ "TXQ of sta %pM tid %d is now empty\n",
+ txq->sta->addr,
+ txq->tid);
+ break;
+ }
+
+ if (!txq->sta)
+ iwl_mvm_tx_skb_non_sta(mvm, skb);
+ else
+ iwl_mvm_tx_skb(mvm, skb, txq->sta);
+ }
+ } while (atomic_dec_return(&mvmtxq->tx_request));
+ rcu_read_unlock();
}
-static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg)
+static void iwl_mvm_mac_wake_tx_queue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
{
- if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG)
- return false;
- if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG)
- return true;
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_txq *mvmtxq = iwl_mvm_txq_from_mac80211(txq);
- /* enabled by default */
- return true;
+ /*
+ * Please note that racing is handled very carefully here:
+ * mvmtxq->txq_id is updated during allocation, and mvmtxq->list is
+ * deleted afterwards.
+ * This means that if:
+ * mvmtxq->txq_id != INVALID_QUEUE && list_empty(&mvmtxq->list):
+ * queue is allocated and we can TX.
+ * mvmtxq->txq_id != INVALID_QUEUE && !list_empty(&mvmtxq->list):
+ * a race, should defer the frame.
+ * mvmtxq->txq_id == INVALID_QUEUE && list_empty(&mvmtxq->list):
+ * need to allocate the queue and defer the frame.
+ * mvmtxq->txq_id == INVALID_QUEUE && !list_empty(&mvmtxq->list):
+ * queue is already scheduled for allocation, no need to allocate,
+ * should defer the frame.
+ */
+
+ /* If the queue is allocated TX and return. */
+ if (!txq->sta || mvmtxq->txq_id != IWL_MVM_INVALID_QUEUE) {
+ /*
+ * Check that list is empty to avoid a race where txq_id is
+ * already updated, but the queue allocation work wasn't
+ * finished
+ */
+ if (unlikely(txq->sta && !list_empty(&mvmtxq->list)))
+ return;
+
+ iwl_mvm_mac_itxq_xmit(hw, txq);
+ return;
+ }
+
+ /* The list is being deleted only after the queue is fully allocated. */
+ if (!list_empty(&mvmtxq->list))
+ return;
+
+ list_add_tail(&mvmtxq->list, &mvm->add_stream_txqs);
+ schedule_work(&mvm->add_stream_wk);
}
#define CHECK_BA_TRIGGER(_mvm, _trig, _tid_bm, _tid, _fmt...) \
@@ -974,7 +1115,7 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
mvmvif = iwl_mvm_vif_from_mac80211(vif);
cancel_delayed_work(&mvmvif->uapsd_nonagg_detected_wk);
}
- if (!iwl_enable_rx_ampdu(mvm->cfg)) {
+ if (!iwl_enable_rx_ampdu()) {
ret = -EINVAL;
break;
}
@@ -986,7 +1127,7 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
timeout);
break;
case IEEE80211_AMPDU_TX_START:
- if (!iwl_enable_tx_ampdu(mvm->cfg)) {
+ if (!iwl_enable_tx_ampdu()) {
ret = -EINVAL;
break;
}
@@ -1066,6 +1207,8 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
iwl_mvm_stop_device(mvm);
+ mvm->cur_aid = 0;
+
mvm->scan_status = 0;
mvm->ps_disabled = false;
mvm->calibrating = false;
@@ -1074,6 +1217,8 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
iwl_mvm_cleanup_roc_te(mvm);
ieee80211_remain_on_channel_expired(mvm->hw);
+ iwl_mvm_ftm_restart(mvm);
+
/*
* cleanup all interfaces, even inactive ones, as some might have
* gone down during the HW restart
@@ -1085,7 +1230,6 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
iwl_mvm_reset_phy_ctxts(mvm);
memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
- memset(mvm->sta_deferred_frames, 0, sizeof(mvm->sta_deferred_frames));
memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
@@ -1188,6 +1332,8 @@ static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
/* allow transport/FW low power modes */
iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
+ iwl_mvm_send_recovery_cmd(mvm, ERROR_RECOVERY_END_OF_RECOVERY);
+
/*
* If we have TDLS peers, remove them. We don't know the last seqno/PN
* of packets the FW sent out, so we must reconnect.
@@ -1391,6 +1537,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
if (ret)
goto out_unlock;
+ rcu_assign_pointer(mvm->vif_id_to_mac[mvmvif->id], vif);
+
/* Counting number of interfaces is needed for legacy PM */
if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
mvm->vif_count++;
@@ -1549,6 +1697,9 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
IEEE80211_VIF_SUPPORTS_CQM_RSSI);
}
+ if (vif->bss_conf.ftm_responder)
+ memset(&mvm->ftm_resp_stats, 0, sizeof(mvm->ftm_resp_stats));
+
iwl_mvm_vif_dbgfs_clean(mvm, vif);
/*
@@ -1582,6 +1733,8 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
iwl_mvm_power_update_mac(mvm);
iwl_mvm_mac_ctxt_remove(mvm, vif);
+ RCU_INIT_POINTER(mvm->vif_id_to_mac[mvmvif->id], NULL);
+
if (vif->type == NL80211_IFTYPE_MONITOR)
mvm->monitor_on = false;
@@ -2076,6 +2229,46 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,
}
flags |= STA_CTXT_HE_PACKET_EXT;
+ } else if ((sta->he_cap.he_cap_elem.phy_cap_info[9] &
+ IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_MASK) !=
+ IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_RESERVED) {
+ int low_th = -1;
+ int high_th = -1;
+
+ /* Take the PPE thresholds from the nominal padding info */
+ switch (sta->he_cap.he_cap_elem.phy_cap_info[9] &
+ IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_MASK) {
+ case IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_0US:
+ low_th = IWL_HE_PKT_EXT_NONE;
+ high_th = IWL_HE_PKT_EXT_NONE;
+ break;
+ case IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_8US:
+ low_th = IWL_HE_PKT_EXT_BPSK;
+ high_th = IWL_HE_PKT_EXT_NONE;
+ break;
+ case IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US:
+ low_th = IWL_HE_PKT_EXT_NONE;
+ high_th = IWL_HE_PKT_EXT_BPSK;
+ break;
+ }
+
+ /* Set the PPE thresholds accordingly */
+ if (low_th >= 0 && high_th >= 0) {
+ u8 ***pkt_ext_qam =
+ (void *)sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th;
+
+ for (i = 0; i < MAX_HE_SUPP_NSS; i++) {
+ u8 bw;
+
+ for (bw = 0; bw < MAX_HE_CHANNEL_BW_INDX;
+ bw++) {
+ pkt_ext_qam[i][bw][0] = low_th;
+ pkt_ext_qam[i][bw][1] = high_th;
+ }
+ }
+
+ flags |= STA_CTXT_HE_PACKET_EXT;
+ }
}
rcu_read_unlock();
@@ -2146,6 +2339,12 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
}
+ /* Update MU EDCA params */
+ if (changes & BSS_CHANGED_QOS && mvmvif->associated &&
+ bss_conf->assoc && vif->bss_conf.he_support &&
+ !iwlwifi_mod_params.disable_11ax)
+ iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->ap_sta_id);
+
/*
* If we're not associated yet, take the (new) BSSID before associating
* so the firmware knows. If we're already associated, then use the old
@@ -2211,7 +2410,10 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
* If update fails - SF might be running in associated
* mode while disassociated - which is forbidden.
*/
- WARN_ONCE(iwl_mvm_sf_update(mvm, vif, false),
+ ret = iwl_mvm_sf_update(mvm, vif, false);
+ WARN_ONCE(ret &&
+ !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,
+ &mvm->status),
"Failed to update SF upon disassociation\n");
/*
@@ -2432,6 +2634,8 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
if (iwl_mvm_phy_ctx_count(mvm) > 1)
iwl_mvm_teardown_tdls_peers(mvm);
+ iwl_mvm_ftm_restart_responder(mvm, vif);
+
goto out_unlock;
out_quota_failed:
@@ -2543,6 +2747,15 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
bss_conf->txpower);
iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower);
}
+
+ if (changes & BSS_CHANGED_FTM_RESPONDER) {
+ int ret = iwl_mvm_ftm_start_responder(mvm, vif);
+
+ if (ret)
+ IWL_WARN(mvm, "Failed to enable FTM responder (%d)\n",
+ ret);
+ }
+
}
static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
@@ -2672,7 +2885,7 @@ static void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
return;
spin_lock_bh(&mvmsta->lock);
- for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ for (tid = 0; tid < ARRAY_SIZE(mvmsta->tid_data); tid++) {
struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
if (tid_data->txq_id == IWL_MVM_INVALID_QUEUE)
@@ -2861,32 +3074,6 @@ iwl_mvm_tdls_check_trigger(struct iwl_mvm *mvm,
peer_addr, action);
}
-static void iwl_mvm_purge_deferred_tx_frames(struct iwl_mvm *mvm,
- struct iwl_mvm_sta *mvm_sta)
-{
- struct iwl_mvm_tid_data *tid_data;
- struct sk_buff *skb;
- int i;
-
- spin_lock_bh(&mvm_sta->lock);
- for (i = 0; i <= IWL_MAX_TID_COUNT; i++) {
- tid_data = &mvm_sta->tid_data[i];
-
- while ((skb = __skb_dequeue(&tid_data->deferred_tx_frames))) {
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
- /*
- * The first deferred frame should've stopped the MAC
- * queues, so we should never get a second deferred
- * frame for the RA/TID.
- */
- iwl_mvm_start_mac_queues(mvm, BIT(info->hw_queue));
- ieee80211_free_txskb(mvm->hw, skb);
- }
- }
- spin_unlock_bh(&mvm_sta->lock);
-}
-
static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
@@ -2920,7 +3107,6 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
*/
if (old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_NOTEXIST) {
- iwl_mvm_purge_deferred_tx_frames(mvm, mvm_sta);
flush_work(&mvm->add_stream_wk);
/*
@@ -2967,6 +3153,8 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
NL80211_TDLS_SETUP);
}
+
+ sta->max_rc_amsdu_len = 1;
} else if (old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_AUTH) {
/*
@@ -2979,11 +3167,15 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
} else if (old_state == IEEE80211_STA_AUTH &&
new_state == IEEE80211_STA_ASSOC) {
if (vif->type == NL80211_IFTYPE_AP) {
+ vif->bss_conf.he_support = sta->he_cap.has_he;
mvmvif->ap_assoc_sta_count++;
iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
if (vif->bss_conf.he_support &&
!iwlwifi_mod_params.disable_11ax)
iwl_mvm_cfg_he_sta(mvm, vif, mvm_sta->sta_id);
+ } else if (vif->type == NL80211_IFTYPE_STATION) {
+ vif->bss_conf.he_support = sta->he_cap.has_he;
+ iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
}
iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band,
@@ -2991,6 +3183,24 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
ret = iwl_mvm_update_sta(mvm, vif, sta);
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTHORIZED) {
+ /* if wep is used, need to set the key for the station now */
+ if (vif->type == NL80211_IFTYPE_AP && mvmvif->ap_wep_key) {
+ mvm_sta->wep_key =
+ kmemdup(mvmvif->ap_wep_key,
+ sizeof(*mvmvif->ap_wep_key) +
+ mvmvif->ap_wep_key->keylen,
+ GFP_KERNEL);
+ if (!mvm_sta->wep_key) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+
+ ret = iwl_mvm_set_sta_key(mvm, vif, sta,
+ mvm_sta->wep_key,
+ STA_KEY_IDX_INVALID);
+ } else {
+ ret = 0;
+ }
/* we don't support TDLS during DCM */
if (iwl_mvm_phy_ctx_count(mvm) > 1)
@@ -3005,18 +3215,13 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band,
true);
-
- /* if wep is used, need to set the key for the station now */
- if (vif->type == NL80211_IFTYPE_AP && mvmvif->ap_wep_key)
- ret = iwl_mvm_set_sta_key(mvm, vif, sta,
- mvmvif->ap_wep_key,
- STA_KEY_IDX_INVALID);
- else
- ret = 0;
} else if (old_state == IEEE80211_STA_AUTHORIZED &&
new_state == IEEE80211_STA_ASSOC) {
/* disable beacon filtering */
- WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, 0));
+ ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
+ WARN_ON(ret &&
+ !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,
+ &mvm->status));
ret = 0;
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTH) {
@@ -3036,6 +3241,22 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
NL80211_TDLS_DISABLE_LINK);
}
+
+ /* Remove STA key if this is an AP using WEP */
+ if (vif->type == NL80211_IFTYPE_AP && mvmvif->ap_wep_key) {
+ int rm_ret = iwl_mvm_remove_sta_key(mvm, vif, sta,
+ mvm_sta->wep_key);
+
+ if (!ret)
+ ret = rm_ret;
+ kfree(mvm_sta->wep_key);
+ mvm_sta->wep_key = NULL;
+ }
+
+ if (unlikely(ret &&
+ test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,
+ &mvm->status)))
+ ret = 0;
} else {
ret = -EIO;
}
@@ -3431,14 +3652,20 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
.id_and_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(MAC_INDEX_AUX, 0)),
.sta_id_and_color = cpu_to_le32(mvm->aux_sta.sta_id),
- /* Set the channel info data */
- .channel_info.band = (channel->band == NL80211_BAND_2GHZ) ?
- PHY_BAND_24 : PHY_BAND_5,
- .channel_info.channel = channel->hw_value,
- .channel_info.width = PHY_VHT_CHANNEL_MODE20,
- /* Set the time and duration */
- .apply_time = cpu_to_le32(iwl_read_prph(mvm->trans, time_reg)),
- };
+ };
+ struct iwl_hs20_roc_req_tail *tail = iwl_mvm_chan_info_cmd_tail(mvm,
+ &aux_roc_req.channel_info);
+ u16 len = sizeof(aux_roc_req) - iwl_mvm_chan_info_padding(mvm);
+
+ /* Set the channel info data */
+ iwl_mvm_set_chan_info(mvm, &aux_roc_req.channel_info, channel->hw_value,
+ (channel->band == NL80211_BAND_2GHZ) ?
+ PHY_BAND_24 : PHY_BAND_5,
+ PHY_VHT_CHANNEL_MODE20,
+ 0);
+
+ /* Set the time and duration */
+ tail->apply_time = cpu_to_le32(iwl_read_prph(mvm->trans, time_reg));
delay = AUX_ROC_MIN_DELAY;
req_dur = MSEC_TO_TU(duration);
@@ -3463,15 +3690,15 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
}
}
- aux_roc_req.duration = cpu_to_le32(req_dur);
- aux_roc_req.apply_time_max_delay = cpu_to_le32(delay);
+ tail->duration = cpu_to_le32(req_dur);
+ tail->apply_time_max_delay = cpu_to_le32(delay);
IWL_DEBUG_TE(mvm,
"ROC: Requesting to remain on channel %u for %ums (requested = %ums, max_delay = %ums, dtim_interval = %ums)\n",
channel->hw_value, req_dur, duration, delay,
dtim_interval);
/* Set the node address */
- memcpy(aux_roc_req.node_addr, vif->addr, ETH_ALEN);
+ memcpy(tail->node_addr, vif->addr, ETH_ALEN);
lockdep_assert_held(&mvm->mutex);
@@ -3502,7 +3729,7 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
ARRAY_SIZE(time_event_response),
iwl_mvm_rx_aux_roc, te_data);
- res = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, sizeof(aux_roc_req),
+ res = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, len,
&aux_roc_req);
if (res) {
@@ -3673,11 +3900,43 @@ static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw)
return 0;
}
+struct iwl_mvm_ftm_responder_iter_data {
+ bool responder;
+ struct ieee80211_chanctx_conf *ctx;
+};
+
+static void iwl_mvm_ftm_responder_chanctx_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_ftm_responder_iter_data *data = _data;
+
+ if (rcu_access_pointer(vif->chanctx_conf) == data->ctx &&
+ vif->type == NL80211_IFTYPE_AP && vif->bss_conf.ftmr_params)
+ data->responder = true;
+}
+
+static bool iwl_mvm_is_ftm_responder_chanctx(struct iwl_mvm *mvm,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct iwl_mvm_ftm_responder_iter_data data = {
+ .responder = false,
+ .ctx = ctx,
+ };
+
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_ftm_responder_chanctx_iter,
+ &data);
+ return data.responder;
+}
+
static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm,
struct ieee80211_chanctx_conf *ctx)
{
u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
struct iwl_mvm_phy_ctxt *phy_ctxt;
+ bool responder = iwl_mvm_is_ftm_responder_chanctx(mvm, ctx);
+ struct cfg80211_chan_def *def = responder ? &ctx->def : &ctx->min_def;
int ret;
lockdep_assert_held(&mvm->mutex);
@@ -3690,7 +3949,7 @@ static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm,
goto out;
}
- ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,
+ ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, def,
ctx->rx_chains_static,
ctx->rx_chains_dynamic);
if (ret) {
@@ -3745,6 +4004,8 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
+ bool responder = iwl_mvm_is_ftm_responder_chanctx(mvm, ctx);
+ struct cfg80211_chan_def *def = responder ? &ctx->def : &ctx->min_def;
if (WARN_ONCE((phy_ctxt->ref > 1) &&
(changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH |
@@ -3759,17 +4020,17 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
/* we are only changing the min_width, may be a noop */
if (changed == IEEE80211_CHANCTX_CHANGE_MIN_WIDTH) {
- if (phy_ctxt->width == ctx->min_def.width)
+ if (phy_ctxt->width == def->width)
goto out_unlock;
/* we are just toggling between 20_NOHT and 20 */
if (phy_ctxt->width <= NL80211_CHAN_WIDTH_20 &&
- ctx->min_def.width <= NL80211_CHAN_WIDTH_20)
+ def->width <= NL80211_CHAN_WIDTH_20)
goto out_unlock;
}
iwl_mvm_bt_coex_vif_change(mvm);
- iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,
+ iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, def,
ctx->rx_chains_static,
ctx->rx_chains_dynamic);
@@ -3798,6 +4059,7 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
mvmvif->ap_ibss_active = true;
break;
}
+ /* fall through */
case NL80211_IFTYPE_ADHOC:
/*
* The AP binding flow is handled as part of the start_ap flow
@@ -3850,25 +4112,30 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
}
if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) {
- u32 duration = 3 * vif->bss_conf.beacon_int;
+ mvmvif->csa_bcn_pending = true;
- /* iwl_mvm_protect_session() reads directly from the
- * device (the system time), so make sure it is
- * available.
- */
- ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_CSA);
- if (ret)
- goto out_remove_binding;
+ if (!fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD)) {
+ u32 duration = 3 * vif->bss_conf.beacon_int;
- /* Protect the session to make sure we hear the first
- * beacon on the new channel.
- */
- mvmvif->csa_bcn_pending = true;
- iwl_mvm_protect_session(mvm, vif, duration, duration,
- vif->bss_conf.beacon_int / 2,
- true);
- iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA);
+ /* iwl_mvm_protect_session() reads directly from the
+ * device (the system time), so make sure it is
+ * available.
+ */
+ ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_CSA);
+ if (ret)
+ goto out_remove_binding;
+
+ /* Protect the session to make sure we hear the first
+ * beacon on the new channel.
+ */
+ iwl_mvm_protect_session(mvm, vif, duration, duration,
+ vif->bss_conf.beacon_int / 2,
+ true);
+
+ iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA);
+ }
iwl_mvm_update_quotas(mvm, false, NULL);
}
@@ -3938,7 +4205,9 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
disabled_vif = vif;
- iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL);
+ if (!fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD))
+ iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL);
break;
default:
break;
@@ -4189,6 +4458,27 @@ static void iwl_mvm_channel_switch(struct ieee80211_hw *hw,
"dummy channel switch op\n");
}
+static int iwl_mvm_schedule_client_csa(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_chan_switch_te_cmd cmd = {
+ .mac_id = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+ mvmvif->color)),
+ .action = cpu_to_le32(FW_CTXT_ACTION_ADD),
+ .tsf = cpu_to_le32(chsw->timestamp),
+ .cs_count = chsw->count,
+ };
+
+ lockdep_assert_held(&mvm->mutex);
+
+ return iwl_mvm_send_cmd_pdu(mvm,
+ WIDE_ID(MAC_CONF_GROUP,
+ CHANNEL_SWITCH_TIME_EVENT_CMD),
+ 0, sizeof(cmd), &cmd);
+}
+
static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel_switch *chsw)
@@ -4256,14 +4546,19 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
if (chsw->block_tx)
iwl_mvm_csa_client_absent(mvm, vif);
- iwl_mvm_schedule_csa_period(mvm, vif, vif->bss_conf.beacon_int,
- apply_time);
if (mvmvif->bf_data.bf_enabled) {
ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
if (ret)
goto out_unlock;
}
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD))
+ iwl_mvm_schedule_client_csa(mvm, vif, chsw);
+ else
+ iwl_mvm_schedule_csa_period(mvm, vif,
+ vif->bss_conf.beacon_int,
+ apply_time);
break;
default:
break;
@@ -4656,8 +4951,89 @@ static void iwl_mvm_sync_rx_queues(struct ieee80211_hw *hw)
mutex_unlock(&mvm->mutex);
}
+static int
+iwl_mvm_mac_get_ftm_responder_stats(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_ftm_responder_stats *stats)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (vif->p2p || vif->type != NL80211_IFTYPE_AP ||
+ !mvmvif->ap_ibss_active || !vif->bss_conf.ftm_responder)
+ return -EINVAL;
+
+ mutex_lock(&mvm->mutex);
+ *stats = mvm->ftm_resp_stats;
+ mutex_unlock(&mvm->mutex);
+
+ stats->filled = BIT(NL80211_FTM_STATS_SUCCESS_NUM) |
+ BIT(NL80211_FTM_STATS_PARTIAL_NUM) |
+ BIT(NL80211_FTM_STATS_FAILED_NUM) |
+ BIT(NL80211_FTM_STATS_ASAP_NUM) |
+ BIT(NL80211_FTM_STATS_NON_ASAP_NUM) |
+ BIT(NL80211_FTM_STATS_TOTAL_DURATION_MSEC) |
+ BIT(NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM) |
+ BIT(NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM) |
+ BIT(NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM);
+
+ return 0;
+}
+
+static int iwl_mvm_start_pmsr(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_pmsr_request *request)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+ ret = iwl_mvm_ftm_start(mvm, vif, request);
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static void iwl_mvm_abort_pmsr(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_pmsr_request *request)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ mutex_lock(&mvm->mutex);
+ iwl_mvm_ftm_abort(mvm, request);
+ mutex_unlock(&mvm->mutex);
+}
+
+static bool iwl_mvm_can_hw_csum(struct sk_buff *skb)
+{
+ u8 protocol = ip_hdr(skb)->protocol;
+
+ if (!IS_ENABLED(CONFIG_INET))
+ return false;
+
+ return protocol == IPPROTO_TCP || protocol == IPPROTO_UDP;
+}
+
+static bool iwl_mvm_mac_can_aggregate(struct ieee80211_hw *hw,
+ struct sk_buff *head,
+ struct sk_buff *skb)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ /* For now don't aggregate IPv6 in AMSDU */
+ if (skb->protocol != htons(ETH_P_IP))
+ return false;
+
+ if (!iwl_mvm_is_csum_supported(mvm))
+ return true;
+
+ return iwl_mvm_can_hw_csum(skb) == iwl_mvm_can_hw_csum(head);
+}
+
const struct ieee80211_ops iwl_mvm_hw_ops = {
.tx = iwl_mvm_mac_tx,
+ .wake_tx_queue = iwl_mvm_mac_wake_tx_queue,
.ampdu_action = iwl_mvm_mac_ampdu_action,
.start = iwl_mvm_mac_start,
.reconfig_complete = iwl_mvm_mac_reconfig_complete,
@@ -4731,6 +5107,11 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
#endif
.get_survey = iwl_mvm_mac_get_survey,
.sta_statistics = iwl_mvm_mac_sta_statistics,
+ .get_ftm_responder_stats = iwl_mvm_mac_get_ftm_responder_stats,
+ .start_pmsr = iwl_mvm_start_pmsr,
+ .abort_pmsr = iwl_mvm_abort_pmsr,
+
+ .can_aggregate_in_amsdu = iwl_mvm_mac_can_aggregate,
#ifdef CONFIG_IWLWIFI_DEBUGFS
.sta_add_debugfs = iwl_mvm_sta_add_debugfs,
#endif