@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2019 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2020 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -52,6 +52,12 @@ enum iwl_data_path_subcmd_ids {
*/
CHEST_COLLECTOR_FILTER_CONFIG_CMD = 0x14,
+ /**
+ * @MONITOR_NOTIF: Datapath monitoring notification, using
+ * &struct iwl_datapath_monitor_notif
+ */
+ MONITOR_NOTIF = 0xF4,
+
/**
* @RX_NO_DATA_NOTIF: &struct iwl_rx_no_data
*/
@@ -153,4 +159,14 @@ struct iwl_channel_estimation_cfg {
__le64 frame_types;
} __packed; /* CHEST_COLLECTOR_FILTER_CMD_API_S_VER_1 */
+enum iwl_datapath_monitor_notif_type {
+ IWL_DP_MON_NOTIF_TYPE_EXT_CCA,
+};
+
+struct iwl_datapath_monitor_notif {
+ __le32 type;
+ u8 mac_id;
+ u8 reserved[3];
+} __packed; /* MONITOR_NTF_API_S_VER_1 */
+
#endif /* __iwl_fw_api_datapath_h__ */
@@ -3009,6 +3009,39 @@ static void iwl_mvm_check_he_obss_narrow_bw_ru(struct ieee80211_hw *hw,
mvmvif->he_ru_2mhz_block = !iter_data.tolerated;
}
+static void iwl_mvm_reset_cca_40mhz_workaround(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct ieee80211_supported_band *sband;
+ const struct ieee80211_sta_he_cap *he_cap;
+
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ if (!mvm->cca_40mhz_workaround)
+ return;
+
+ /* decrement and check that we reached zero */
+ mvm->cca_40mhz_workaround--;
+ if (mvm->cca_40mhz_workaround)
+ return;
+
+ sband = mvm->hw->wiphy->bands[NL80211_BAND_2GHZ];
+
+ sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+ he_cap = ieee80211_get_he_iftype_cap(sband,
+ ieee80211_vif_type_p2p(vif));
+
+ if (he_cap) {
+ /* we know that ours is writable */
+ struct ieee80211_sta_he_cap *he = (void *)he_cap;
+
+ he->he_cap_elem.phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+ }
+}
+
static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
@@ -3048,6 +3081,12 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
* No need to make sure deferred TX indication is off since the
* worker will already remove it if it was on
*/
+
+ /*
+ * Additionally, reset the 40 MHz capability if we disconnected
+ * from the AP now.
+ */
+ iwl_mvm_reset_cca_40mhz_workaround(mvm, vif);
}
mutex_lock(&mvm->mutex);
@@ -796,6 +796,8 @@ struct iwl_mvm {
bool hw_registered;
bool rfkill_safe_init_done;
+ u8 cca_40mhz_workaround;
+
u32 ampdu_ref;
bool ampdu_toggle;
@@ -1995,6 +1997,7 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm,
u32 size);
void iwl_mvm_reorder_timer_expired(struct timer_list *t);
struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm);
+struct ieee80211_vif *iwl_mvm_get_vif_by_macid(struct iwl_mvm *mvm, u32 macid);
bool iwl_mvm_is_vif_assoc(struct iwl_mvm *mvm);
#define MVM_TCM_PERIOD_MSEC 500
@@ -146,6 +146,70 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
}
+static void iwl_mvm_rx_monitor_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_datapath_monitor_notif *notif = (void *)pkt->data;
+ struct ieee80211_supported_band *sband;
+ const struct ieee80211_sta_he_cap *he_cap;
+ struct ieee80211_vif *vif;
+
+ if (notif->type != cpu_to_le32(IWL_DP_MON_NOTIF_TYPE_EXT_CCA))
+ return;
+
+ vif = iwl_mvm_get_vif_by_macid(mvm, notif->mac_id);
+ if (!vif || vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ if (!vif->bss_conf.chandef.chan ||
+ vif->bss_conf.chandef.chan->band != NL80211_BAND_2GHZ ||
+ vif->bss_conf.chandef.width < NL80211_CHAN_WIDTH_40)
+ return;
+
+ if (!vif->bss_conf.assoc)
+ return;
+
+ /* this shouldn't happen *again*, ignore it */
+ if (mvm->cca_40mhz_workaround)
+ return;
+
+ /*
+ * We'll decrement this on disconnect - so set to 2 since we'll
+ * still have to disconnect from the current AP first.
+ */
+ mvm->cca_40mhz_workaround = 2;
+
+ /*
+ * This capability manipulation isn't really ideal, but it's the
+ * easiest choice - otherwise we'd have to do some major changes
+ * in mac80211 to support this, which isn't worth it. This does
+ * mean that userspace may have outdated information, but that's
+ * actually not an issue at all.
+ */
+ sband = mvm->hw->wiphy->bands[NL80211_BAND_2GHZ];
+
+ WARN_ON(!sband->ht_cap.ht_supported);
+ WARN_ON(!(sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40));
+ sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+ he_cap = ieee80211_get_he_iftype_cap(sband,
+ ieee80211_vif_type_p2p(vif));
+
+ if (he_cap) {
+ /* we know that ours is writable */
+ struct ieee80211_sta_he_cap *he = (void *)he_cap;
+
+ WARN_ON(!he->has_he);
+ WARN_ON(!(he->he_cap_elem.phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G));
+ he->he_cap_elem.phy_cap_info[0] &=
+ ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+ }
+
+ ieee80211_disconnect(vif, true);
+}
+
/**
* enum iwl_rx_handler_context context for Rx handler
* @RX_HANDLER_SYNC : this means that it will be called in the Rx path
@@ -266,6 +330,9 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
RX_HANDLER_GRP(MAC_CONF_GROUP, CHANNEL_SWITCH_NOA_NOTIF,
iwl_mvm_channel_switch_noa_notif,
RX_HANDLER_SYNC),
+ RX_HANDLER_GRP(DATA_PATH_GROUP, MONITOR_NOTIF,
+ iwl_mvm_rx_monitor_notif,
+ RX_HANDLER_ASYNC_LOCKED),
};
#undef RX_HANDLER
#undef RX_HANDLER_GRP
@@ -410,6 +477,7 @@ static const struct iwl_hcmd_names iwl_mvm_data_path_names[] = {
HCMD_NAME(RFH_QUEUE_CONFIG_CMD),
HCMD_NAME(TLC_MNG_CONFIG_CMD),
HCMD_NAME(CHEST_COLLECTOR_FILTER_CONFIG_CMD),
+ HCMD_NAME(MONITOR_NOTIF),
HCMD_NAME(STA_PM_NOTIF),
HCMD_NAME(MU_GROUP_MGMT_NOTIF),
HCMD_NAME(RX_QUEUES_NOTIFICATION),
@@ -832,6 +832,36 @@ struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm)
return bss_iter_data.vif;
}
+struct iwl_bss_find_iter_data {
+ struct ieee80211_vif *vif;
+ u32 macid;
+};
+
+static void iwl_mvm_bss_find_iface_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_bss_find_iter_data *data = _data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (mvmvif->id == data->macid)
+ data->vif = vif;
+}
+
+struct ieee80211_vif *iwl_mvm_get_vif_by_macid(struct iwl_mvm *mvm, u32 macid)
+{
+ struct iwl_bss_find_iter_data data = {
+ .macid = macid,
+ };
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_bss_find_iface_iterator, &data);
+
+ return data.vif;
+}
+
struct iwl_sta_iter_data {
bool assoc;
};