Commit 1d3c3f63 authored by Arik Nemtsov's avatar Arik Nemtsov Committed by Emmanuel Grumbach

iwlwifi: mvm: implement mac80211 TDLS channel-switch APIs

Maintain a TDLS channel-switch state and update it according to
notifications from FW and timeouts. Explicitly check all state
transitions are valid.
When switching is initiated by mac80211, use a delayed work to
periodically reschedule it from iwlwifi.
Give the FW mac80211 generated TDLS channel-switch request/response
templates. It will change appropriate values (switch timings) and Tx
them at appropriate times.

Enable the channel switch wiphy capability bit when the FW supports it.
Signed-off-by: default avatarArik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
parent 9c126cd6
...@@ -466,6 +466,8 @@ int iwl_mvm_up(struct iwl_mvm *mvm) ...@@ -466,6 +466,8 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
for (i = 0; i < IWL_MVM_STATION_COUNT; i++) for (i = 0; i < IWL_MVM_STATION_COUNT; i++)
RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
/* reset quota debouncing buffer - 0xff will yield invalid data */ /* reset quota debouncing buffer - 0xff will yield invalid data */
memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd)); memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd));
......
...@@ -495,6 +495,12 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) ...@@ -495,6 +495,12 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
} }
if (mvm->fw->ucode_capa.capa[0] &
IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH) {
IWL_DEBUG_TDLS(mvm, "TDLS channel switch supported\n");
hw->wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
}
ret = ieee80211_register_hw(mvm->hw); ret = ieee80211_register_hw(mvm->hw);
if (ret) if (ret)
iwl_mvm_leds_exit(mvm); iwl_mvm_leds_exit(mvm);
...@@ -3211,6 +3217,10 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { ...@@ -3211,6 +3217,10 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
.channel_switch_beacon = iwl_mvm_channel_switch_beacon, .channel_switch_beacon = iwl_mvm_channel_switch_beacon,
.tdls_channel_switch = iwl_mvm_tdls_channel_switch,
.tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch,
.tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch,
CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
......
...@@ -519,6 +519,13 @@ enum { ...@@ -519,6 +519,13 @@ enum {
#define IWL_MVM_DEBUG_SET_TEMPERATURE_MIN -100 #define IWL_MVM_DEBUG_SET_TEMPERATURE_MIN -100
#define IWL_MVM_DEBUG_SET_TEMPERATURE_MAX 200 #define IWL_MVM_DEBUG_SET_TEMPERATURE_MAX 200
enum iwl_mvm_tdls_cs_state {
IWL_MVM_TDLS_SW_IDLE = 0,
IWL_MVM_TDLS_SW_REQ_SENT,
IWL_MVM_TDLS_SW_REQ_RCVD,
IWL_MVM_TDLS_SW_ACTIVE,
};
struct iwl_mvm { struct iwl_mvm {
/* for logger access */ /* for logger access */
struct device *dev; struct device *dev;
...@@ -740,6 +747,28 @@ struct iwl_mvm { ...@@ -740,6 +747,28 @@ struct iwl_mvm {
u32 ap_last_beacon_gp2; u32 ap_last_beacon_gp2;
u8 low_latency_agg_frame_limit; u8 low_latency_agg_frame_limit;
/* TDLS channel switch data */
struct {
struct delayed_work dwork;
enum iwl_mvm_tdls_cs_state state;
/*
* Current cs sta - might be different from periodic cs peer
* station. Value is meaningless when the cs-state is idle.
*/
u8 cur_sta_id;
/* TDLS periodic channel-switch peer */
struct {
u8 sta_id;
u8 op_class;
bool initiator; /* are we the link initiator */
struct cfg80211_chan_def chandef;
struct sk_buff *skb; /* ch sw template */
u32 ch_sw_tm_ie;
} peer;
} tdls_cs;
}; };
/* Extract MVM priv from op_mode and _hw */ /* Extract MVM priv from op_mode and _hw */
...@@ -1258,6 +1287,20 @@ void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif, ...@@ -1258,6 +1287,20 @@ void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
bool sta_added); bool sta_added);
void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw, void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
struct ieee80211_vif *vif); struct ieee80211_vif *vif);
int iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u8 oper_class,
struct cfg80211_chan_def *chandef,
struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie);
void iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_tdls_ch_sw_params *params);
void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
int iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
void iwl_mvm_tdls_ch_switch_work(struct work_struct *work);
struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm); struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm);
......
...@@ -258,6 +258,9 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { ...@@ -258,6 +258,9 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
iwl_mvm_power_uapsd_misbehaving_ap_notif, false), iwl_mvm_power_uapsd_misbehaving_ap_notif, false),
RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif, true), RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif, true),
RX_HANDLER(TDLS_CHANNEL_SWITCH_NOTIFICATION, iwl_mvm_rx_tdls_notif,
true),
}; };
#undef RX_HANDLER #undef RX_HANDLER
#define CMD(x) [x] = #x #define CMD(x) [x] = #x
...@@ -453,6 +456,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, ...@@ -453,6 +456,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk); INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk);
INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work); INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work);
INIT_WORK(&mvm->fw_error_dump_wk, iwl_mvm_fw_error_dump_wk); INIT_WORK(&mvm->fw_error_dump_wk, iwl_mvm_fw_error_dump_wk);
INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work);
spin_lock_init(&mvm->d0i3_tx_lock); spin_lock_init(&mvm->d0i3_tx_lock);
spin_lock_init(&mvm->refs_lock); spin_lock_init(&mvm->refs_lock);
......
...@@ -503,6 +503,15 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, ...@@ -503,6 +503,15 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
} }
/*
* This shouldn't happen - the TDLS channel switch should be canceled
* before the STA is removed.
*/
if (WARN_ON_ONCE(mvm->tdls_cs.peer.sta_id == mvm_sta->sta_id)) {
mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
cancel_delayed_work(&mvm->tdls_cs.dwork);
}
/* /*
* Make sure that the tx response code sees the station as -EBUSY and * Make sure that the tx response code sees the station as -EBUSY and
* calls the drain worker. * calls the drain worker.
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment