Commit d51c2ea3 authored by Arik Nemtsov's avatar Arik Nemtsov Committed by Johannes Berg

mac80211: TDLS: correctly configure SMPS state

The IEEE802.11-2012 specification is vague regarding SMPS operation during
TDLS. It does not define a clear way to transition between SMPS states.

To avoid interop issues, set SMPS to off when TDLS peers are connected.
Accomplish this by extending the definition of the AUTOMATIC state. If the
driver forces a state other than OFF, disconnect all TDLS peers.

While at it, avoid changing the SMPS state of the peer STA. We have no
way to control it, so try and behave correctly towards it.

Move the TDLS peer-teardown function to where the rest of the TDLS code
resides.
Signed-off-by: default avatarArik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 3633ebeb
...@@ -2368,6 +2368,8 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, ...@@ -2368,6 +2368,8 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
const u8 *ap; const u8 *ap;
enum ieee80211_smps_mode old_req; enum ieee80211_smps_mode old_req;
int err; int err;
struct sta_info *sta;
bool tdls_peer_found = false;
lockdep_assert_held(&sdata->wdev.mtx); lockdep_assert_held(&sdata->wdev.mtx);
...@@ -2392,11 +2394,22 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, ...@@ -2392,11 +2394,22 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
ap = sdata->u.mgd.associated->bssid; ap = sdata->u.mgd.associated->bssid;
rcu_read_lock();
list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
continue;
tdls_peer_found = true;
break;
}
rcu_read_unlock();
if (smps_mode == IEEE80211_SMPS_AUTOMATIC) { if (smps_mode == IEEE80211_SMPS_AUTOMATIC) {
if (sdata->u.mgd.powersave) if (tdls_peer_found || !sdata->u.mgd.powersave)
smps_mode = IEEE80211_SMPS_DYNAMIC;
else
smps_mode = IEEE80211_SMPS_OFF; smps_mode = IEEE80211_SMPS_OFF;
else
smps_mode = IEEE80211_SMPS_DYNAMIC;
} }
/* send SM PS frame to AP */ /* send SM PS frame to AP */
...@@ -2404,6 +2417,8 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, ...@@ -2404,6 +2417,8 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
ap, ap); ap, ap);
if (err) if (err)
sdata->u.mgd.req_smps = old_req; sdata->u.mgd.req_smps = old_req;
else if (smps_mode != IEEE80211_SMPS_OFF && tdls_peer_found)
ieee80211_teardown_tdls_peers(sdata);
return err; return err;
} }
......
...@@ -2054,6 +2054,7 @@ void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy, ...@@ -2054,6 +2054,7 @@ void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
const u8 *addr); const u8 *addr);
void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata, void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb); struct sk_buff *skb);
void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata);
extern const struct ethtool_ops ieee80211_ethtool_ops; extern const struct ethtool_ops ieee80211_ethtool_ops;
......
...@@ -1096,24 +1096,6 @@ static void ieee80211_chswitch_timer(unsigned long data) ...@@ -1096,24 +1096,6 @@ static void ieee80211_chswitch_timer(unsigned long data)
ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work); ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work);
} }
static void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata)
{
struct sta_info *sta;
u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
rcu_read_lock();
list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
continue;
ieee80211_tdls_oper_request(&sdata->vif, sta->sta.addr,
NL80211_TDLS_TEARDOWN, reason,
GFP_ATOMIC);
}
rcu_read_unlock();
}
static void static void
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
u64 timestamp, u32 device_timestamp, u64 timestamp, u32 device_timestamp,
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2014, Intel Corporation * Copyright 2014, Intel Corporation
* Copyright 2014 Intel Mobile Communications GmbH * Copyright 2014 Intel Mobile Communications GmbH
* Copyright 2015 Intel Deutschland GmbH
* *
* This file is GPLv2 as found in COPYING. * This file is GPLv2 as found in COPYING.
*/ */
...@@ -448,10 +449,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, ...@@ -448,10 +449,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap); ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
} else if (action_code == WLAN_TDLS_SETUP_RESPONSE && } else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) { ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
/* disable SMPS in TDLS responder */
sta->sta.ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED
<< IEEE80211_HT_CAP_SM_PS_SHIFT;
/* the peer caps are already intersected with our own */ /* the peer caps are already intersected with our own */
memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap)); memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
...@@ -1063,8 +1060,17 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, ...@@ -1063,8 +1060,17 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
{ {
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
enum ieee80211_smps_mode smps_mode = sdata->u.mgd.driver_smps_mode;
int ret; int ret;
/* don't support setup with forced SMPS mode that's not off */
if (smps_mode != IEEE80211_SMPS_AUTOMATIC &&
smps_mode != IEEE80211_SMPS_OFF) {
tdls_dbg(sdata, "Aborting TDLS setup due to SMPS mode %d\n",
smps_mode);
return -ENOTSUPP;
}
mutex_lock(&local->mtx); mutex_lock(&local->mtx);
/* we don't support concurrent TDLS peer setups */ /* we don't support concurrent TDLS peer setups */
...@@ -1323,6 +1329,10 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, ...@@ -1323,6 +1329,10 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
eth_zero_addr(sdata->u.mgd.tdls_peer); eth_zero_addr(sdata->u.mgd.tdls_peer);
} }
if (ret == 0)
ieee80211_queue_work(&sdata->local->hw,
&sdata->u.mgd.request_smps_work);
mutex_unlock(&local->mtx); mutex_unlock(&local->mtx);
return ret; return ret;
} }
...@@ -1819,3 +1829,21 @@ void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata, ...@@ -1819,3 +1829,21 @@ void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
return; return;
} }
} }
void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata)
{
struct sta_info *sta;
u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
rcu_read_lock();
list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
continue;
ieee80211_tdls_oper_request(&sdata->vif, sta->sta.addr,
NL80211_TDLS_TEARDOWN, reason,
GFP_ATOMIC);
}
rcu_read_unlock();
}
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