diff options
| author | Arik Nemtsov <arik@wizery.com> | 2014-11-09 11:50:19 -0500 |
|---|---|---|
| committer | Johannes Berg <johannes.berg@intel.com> | 2014-11-19 12:45:21 -0500 |
| commit | a7a6bdd0670feb8bfc26d41cda32b6064dbca50e (patch) | |
| tree | d343591f508af2d1a15f5b265e7ddaf182e34129 /net | |
| parent | 53837584438f8899e061ada4663ae1d09b49b96a (diff) | |
mac80211: introduce TDLS channel switch ops
Implement the cfg80211 TDLS channel switch ops and introduce new mac80211
ones for low-level drivers.
Verify low-level driver support for the new ops when using the relevant
wiphy feature bit. Also verify the peer supports channel switching before
passing the command down.
Add a new STA flag to track the off-channel state with the TDLS peer and
make sure to cancel the channel-switch if the peer STA is unexpectedly
removed.
Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: Arik Nemtsov <arik@wizery.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
| -rw-r--r-- | net/mac80211/cfg.c | 2 | ||||
| -rw-r--r-- | net/mac80211/debugfs_sta.c | 10 | ||||
| -rw-r--r-- | net/mac80211/driver-ops.h | 41 | ||||
| -rw-r--r-- | net/mac80211/ieee80211_i.h | 6 | ||||
| -rw-r--r-- | net/mac80211/main.c | 5 | ||||
| -rw-r--r-- | net/mac80211/sta_info.c | 9 | ||||
| -rw-r--r-- | net/mac80211/sta_info.h | 3 | ||||
| -rw-r--r-- | net/mac80211/tdls.c | 234 | ||||
| -rw-r--r-- | net/mac80211/trace.h | 57 |
9 files changed, 362 insertions, 5 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 8195e65d8a91..e75d5c53e97b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
| @@ -3752,6 +3752,8 @@ const struct cfg80211_ops mac80211_config_ops = { | |||
| 3752 | .set_rekey_data = ieee80211_set_rekey_data, | 3752 | .set_rekey_data = ieee80211_set_rekey_data, |
| 3753 | .tdls_oper = ieee80211_tdls_oper, | 3753 | .tdls_oper = ieee80211_tdls_oper, |
| 3754 | .tdls_mgmt = ieee80211_tdls_mgmt, | 3754 | .tdls_mgmt = ieee80211_tdls_mgmt, |
| 3755 | .tdls_channel_switch = ieee80211_tdls_channel_switch, | ||
| 3756 | .tdls_cancel_channel_switch = ieee80211_tdls_cancel_channel_switch, | ||
| 3755 | .probe_client = ieee80211_probe_client, | 3757 | .probe_client = ieee80211_probe_client, |
| 3756 | .set_noack_map = ieee80211_set_noack_map, | 3758 | .set_noack_map = ieee80211_set_noack_map, |
| 3757 | #ifdef CONFIG_PM | 3759 | #ifdef CONFIG_PM |
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 2ba7f53746da..94c70091bbd7 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c | |||
| @@ -74,7 +74,7 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf, | |||
| 74 | test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : "" | 74 | test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : "" |
| 75 | 75 | ||
| 76 | int res = scnprintf(buf, sizeof(buf), | 76 | int res = scnprintf(buf, sizeof(buf), |
| 77 | "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", | 77 | "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", |
| 78 | TEST(AUTH), TEST(ASSOC), TEST(PS_STA), | 78 | TEST(AUTH), TEST(ASSOC), TEST(PS_STA), |
| 79 | TEST(PS_DRIVER), TEST(AUTHORIZED), | 79 | TEST(PS_DRIVER), TEST(AUTHORIZED), |
| 80 | TEST(SHORT_PREAMBLE), | 80 | TEST(SHORT_PREAMBLE), |
| @@ -83,10 +83,10 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf, | |||
| 83 | TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL), | 83 | TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL), |
| 84 | TEST(UAPSD), TEST(SP), TEST(TDLS_PEER), | 84 | TEST(UAPSD), TEST(SP), TEST(TDLS_PEER), |
| 85 | TEST(TDLS_PEER_AUTH), TEST(TDLS_INITIATOR), | 85 | TEST(TDLS_PEER_AUTH), TEST(TDLS_INITIATOR), |
| 86 | TEST(TDLS_CHAN_SWITCH), TEST(4ADDR_EVENT), | 86 | TEST(TDLS_CHAN_SWITCH), TEST(TDLS_OFF_CHANNEL), |
| 87 | TEST(INSERTED), TEST(RATE_CONTROL), | 87 | TEST(4ADDR_EVENT), TEST(INSERTED), |
| 88 | TEST(TOFFSET_KNOWN), TEST(MPSP_OWNER), | 88 | TEST(RATE_CONTROL), TEST(TOFFSET_KNOWN), |
| 89 | TEST(MPSP_RECIPIENT)); | 89 | TEST(MPSP_OWNER), TEST(MPSP_RECIPIENT)); |
| 90 | #undef TEST | 90 | #undef TEST |
| 91 | return simple_read_from_buffer(userbuf, count, ppos, buf, res); | 91 | return simple_read_from_buffer(userbuf, count, ppos, buf, res); |
| 92 | } | 92 | } |
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 9759dd1f0734..ec4ae42ac15f 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h | |||
| @@ -1296,4 +1296,45 @@ static inline int drv_get_txpower(struct ieee80211_local *local, | |||
| 1296 | return ret; | 1296 | return ret; |
| 1297 | } | 1297 | } |
| 1298 | 1298 | ||
| 1299 | static inline int | ||
| 1300 | drv_tdls_channel_switch(struct ieee80211_local *local, | ||
| 1301 | struct ieee80211_sub_if_data *sdata, | ||
| 1302 | struct ieee80211_sta *sta, u8 oper_class, | ||
| 1303 | struct cfg80211_chan_def *chandef, | ||
| 1304 | struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie) | ||
| 1305 | { | ||
| 1306 | int ret; | ||
| 1307 | |||
| 1308 | might_sleep(); | ||
| 1309 | if (!check_sdata_in_driver(sdata)) | ||
| 1310 | return -EIO; | ||
| 1311 | |||
| 1312 | if (!local->ops->tdls_channel_switch) | ||
| 1313 | return -EOPNOTSUPP; | ||
| 1314 | |||
| 1315 | trace_drv_tdls_channel_switch(local, sdata, sta, oper_class, chandef); | ||
| 1316 | ret = local->ops->tdls_channel_switch(&local->hw, &sdata->vif, sta, | ||
| 1317 | oper_class, chandef, tmpl_skb, | ||
| 1318 | ch_sw_tm_ie); | ||
| 1319 | trace_drv_return_int(local, ret); | ||
| 1320 | return ret; | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | static inline void | ||
| 1324 | drv_tdls_cancel_channel_switch(struct ieee80211_local *local, | ||
| 1325 | struct ieee80211_sub_if_data *sdata, | ||
| 1326 | struct ieee80211_sta *sta) | ||
| 1327 | { | ||
| 1328 | might_sleep(); | ||
| 1329 | if (!check_sdata_in_driver(sdata)) | ||
| 1330 | return; | ||
| 1331 | |||
| 1332 | if (!local->ops->tdls_cancel_channel_switch) | ||
| 1333 | return; | ||
| 1334 | |||
| 1335 | trace_drv_tdls_cancel_channel_switch(local, sdata, sta); | ||
| 1336 | local->ops->tdls_cancel_channel_switch(&local->hw, &sdata->vif, sta); | ||
| 1337 | trace_drv_return_void(local); | ||
| 1338 | } | ||
| 1339 | |||
| 1299 | #endif /* __MAC80211_DRIVER_OPS */ | 1340 | #endif /* __MAC80211_DRIVER_OPS */ |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e786ab6bc72c..2c7abc077b6b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
| @@ -2007,6 +2007,12 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, | |||
| 2007 | int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, | 2007 | int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, |
| 2008 | const u8 *peer, enum nl80211_tdls_operation oper); | 2008 | const u8 *peer, enum nl80211_tdls_operation oper); |
| 2009 | void ieee80211_tdls_peer_del_work(struct work_struct *wk); | 2009 | void ieee80211_tdls_peer_del_work(struct work_struct *wk); |
| 2010 | int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev, | ||
| 2011 | const u8 *addr, u8 oper_class, | ||
| 2012 | struct cfg80211_chan_def *chandef); | ||
| 2013 | void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy, | ||
| 2014 | struct net_device *dev, | ||
| 2015 | const u8 *addr); | ||
| 2010 | 2016 | ||
| 2011 | extern const struct ethtool_ops ieee80211_ethtool_ops; | 2017 | extern const struct ethtool_ops ieee80211_ethtool_ops; |
| 2012 | 2018 | ||
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 282a4f36eb92..774ccb2d9a76 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
| @@ -764,6 +764,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
| 764 | local->hw.offchannel_tx_hw_queue >= local->hw.queues)) | 764 | local->hw.offchannel_tx_hw_queue >= local->hw.queues)) |
| 765 | return -EINVAL; | 765 | return -EINVAL; |
| 766 | 766 | ||
| 767 | if ((hw->wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) && | ||
| 768 | (!local->ops->tdls_channel_switch || | ||
| 769 | !local->ops->tdls_cancel_channel_switch)) | ||
| 770 | return -EOPNOTSUPP; | ||
| 771 | |||
| 767 | #ifdef CONFIG_PM | 772 | #ifdef CONFIG_PM |
| 768 | if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume)) | 773 | if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume)) |
| 769 | return -EINVAL; | 774 | return -EINVAL; |
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 97372514f287..86ca62765699 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
| @@ -847,6 +847,15 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta) | |||
| 847 | if (WARN_ON(ret)) | 847 | if (WARN_ON(ret)) |
| 848 | return ret; | 848 | return ret; |
| 849 | 849 | ||
| 850 | /* | ||
| 851 | * for TDLS peers, make sure to return to the base channel before | ||
| 852 | * removal. | ||
| 853 | */ | ||
| 854 | if (test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) { | ||
| 855 | drv_tdls_cancel_channel_switch(local, sdata, &sta->sta); | ||
| 856 | clear_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL); | ||
| 857 | } | ||
| 858 | |||
| 850 | list_del_rcu(&sta->list); | 859 | list_del_rcu(&sta->list); |
| 851 | 860 | ||
| 852 | drv_sta_pre_rcu_remove(local, sta->sdata, sta); | 861 | drv_sta_pre_rcu_remove(local, sta->sdata, sta); |
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index b6702c810ad3..00f56eb72c60 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
| @@ -50,6 +50,8 @@ | |||
| 50 | * @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this | 50 | * @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this |
| 51 | * station. | 51 | * station. |
| 52 | * @WLAN_STA_TDLS_CHAN_SWITCH: This TDLS peer supports TDLS channel-switching | 52 | * @WLAN_STA_TDLS_CHAN_SWITCH: This TDLS peer supports TDLS channel-switching |
| 53 | * @WLAN_STA_TDLS_OFF_CHANNEL: The local STA is currently off-channel with this | ||
| 54 | * TDLS peer | ||
| 53 | * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was | 55 | * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was |
| 54 | * keeping station in power-save mode, reply when the driver | 56 | * keeping station in power-save mode, reply when the driver |
| 55 | * unblocks the station. | 57 | * unblocks the station. |
| @@ -80,6 +82,7 @@ enum ieee80211_sta_info_flags { | |||
| 80 | WLAN_STA_TDLS_PEER_AUTH, | 82 | WLAN_STA_TDLS_PEER_AUTH, |
| 81 | WLAN_STA_TDLS_INITIATOR, | 83 | WLAN_STA_TDLS_INITIATOR, |
| 82 | WLAN_STA_TDLS_CHAN_SWITCH, | 84 | WLAN_STA_TDLS_CHAN_SWITCH, |
| 85 | WLAN_STA_TDLS_OFF_CHANNEL, | ||
| 83 | WLAN_STA_UAPSD, | 86 | WLAN_STA_UAPSD, |
| 84 | WLAN_STA_SP, | 87 | WLAN_STA_SP, |
| 85 | WLAN_STA_4ADDR_EVENT, | 88 | WLAN_STA_4ADDR_EVENT, |
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index fa141aecd986..358f9a4512ad 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c | |||
| @@ -449,6 +449,48 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, | |||
| 449 | ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); | 449 | ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); |
| 450 | } | 450 | } |
| 451 | 451 | ||
| 452 | static void | ||
| 453 | ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_sub_if_data *sdata, | ||
| 454 | struct sk_buff *skb, const u8 *peer, | ||
| 455 | bool initiator, const u8 *extra_ies, | ||
| 456 | size_t extra_ies_len, u8 oper_class, | ||
| 457 | struct cfg80211_chan_def *chandef) | ||
| 458 | { | ||
| 459 | struct ieee80211_tdls_data *tf; | ||
