Skip to content

Commit

Permalink
mac80211: introduce TDLS channel switch ops
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Arik Nemtsov authored and Johannes Berg committed Nov 19, 2014
1 parent 5383758 commit a7a6bdd
Show file tree
Hide file tree
Showing 10 changed files with 381 additions and 5 deletions.
19 changes: 19 additions & 0 deletions include/net/mac80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -2915,6 +2915,16 @@ enum ieee80211_reconfig_type {
*
* @get_txpower: get current maximum tx power (in dBm) based on configuration
* and hardware limits.
*
* @tdls_channel_switch: Start channel-switching with a TDLS peer. The driver
* is responsible for continually initiating channel-switching operations
* and returning to the base channel for communication with the AP. The
* driver receives a channel-switch request template and the location of
* the switch-timing IE within the template as part of the invocation.
* The template is valid only within the call, and the driver can
* optionally copy the skb for further re-use.
* @tdls_cancel_channel_switch: Stop channel-switching with a TDLS peer. Both
* peers must be on the base channel when the call completes.
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
Expand Down Expand Up @@ -3126,6 +3136,15 @@ struct ieee80211_ops {
u32 (*get_expected_throughput)(struct ieee80211_sta *sta);
int (*get_txpower)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
int *dbm);

int (*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 *skb, u32 ch_sw_tm_ie);
void (*tdls_cancel_channel_switch)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
};

/**
Expand Down
2 changes: 2 additions & 0 deletions net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -3752,6 +3752,8 @@ const struct cfg80211_ops mac80211_config_ops = {
.set_rekey_data = ieee80211_set_rekey_data,
.tdls_oper = ieee80211_tdls_oper,
.tdls_mgmt = ieee80211_tdls_mgmt,
.tdls_channel_switch = ieee80211_tdls_channel_switch,
.tdls_cancel_channel_switch = ieee80211_tdls_cancel_channel_switch,
.probe_client = ieee80211_probe_client,
.set_noack_map = ieee80211_set_noack_map,
#ifdef CONFIG_PM
Expand Down
10 changes: 5 additions & 5 deletions net/mac80211/debugfs_sta.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : ""

int res = scnprintf(buf, sizeof(buf),
"%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%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
TEST(AUTH), TEST(ASSOC), TEST(PS_STA),
TEST(PS_DRIVER), TEST(AUTHORIZED),
TEST(SHORT_PREAMBLE),
Expand All @@ -83,10 +83,10 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL),
TEST(UAPSD), TEST(SP), TEST(TDLS_PEER),
TEST(TDLS_PEER_AUTH), TEST(TDLS_INITIATOR),
TEST(TDLS_CHAN_SWITCH), TEST(4ADDR_EVENT),
TEST(INSERTED), TEST(RATE_CONTROL),
TEST(TOFFSET_KNOWN), TEST(MPSP_OWNER),
TEST(MPSP_RECIPIENT));
TEST(TDLS_CHAN_SWITCH), TEST(TDLS_OFF_CHANNEL),
TEST(4ADDR_EVENT), TEST(INSERTED),
TEST(RATE_CONTROL), TEST(TOFFSET_KNOWN),
TEST(MPSP_OWNER), TEST(MPSP_RECIPIENT));
#undef TEST
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
Expand Down
41 changes: 41 additions & 0 deletions net/mac80211/driver-ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -1296,4 +1296,45 @@ static inline int drv_get_txpower(struct ieee80211_local *local,
return ret;
}

static inline int
drv_tdls_channel_switch(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta, u8 oper_class,
struct cfg80211_chan_def *chandef,
struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie)
{
int ret;

might_sleep();
if (!check_sdata_in_driver(sdata))
return -EIO;

if (!local->ops->tdls_channel_switch)
return -EOPNOTSUPP;

trace_drv_tdls_channel_switch(local, sdata, sta, oper_class, chandef);
ret = local->ops->tdls_channel_switch(&local->hw, &sdata->vif, sta,
oper_class, chandef, tmpl_skb,
ch_sw_tm_ie);
trace_drv_return_int(local, ret);
return ret;
}

static inline void
drv_tdls_cancel_channel_switch(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta)
{
might_sleep();
if (!check_sdata_in_driver(sdata))
return;

if (!local->ops->tdls_cancel_channel_switch)
return;

trace_drv_tdls_cancel_channel_switch(local, sdata, sta);
local->ops->tdls_cancel_channel_switch(&local->hw, &sdata->vif, sta);
trace_drv_return_void(local);
}

#endif /* __MAC80211_DRIVER_OPS */
6 changes: 6 additions & 0 deletions net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -2007,6 +2007,12 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
const u8 *peer, enum nl80211_tdls_operation oper);
void ieee80211_tdls_peer_del_work(struct work_struct *wk);
int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
const u8 *addr, u8 oper_class,
struct cfg80211_chan_def *chandef);
void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
struct net_device *dev,
const u8 *addr);

extern const struct ethtool_ops ieee80211_ethtool_ops;

Expand Down
5 changes: 5 additions & 0 deletions net/mac80211/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
local->hw.offchannel_tx_hw_queue >= local->hw.queues))
return -EINVAL;

if ((hw->wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) &&
(!local->ops->tdls_channel_switch ||
!local->ops->tdls_cancel_channel_switch))
return -EOPNOTSUPP;

#ifdef CONFIG_PM
if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume))
return -EINVAL;
Expand Down
9 changes: 9 additions & 0 deletions net/mac80211/sta_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,15 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
if (WARN_ON(ret))
return ret;

/*
* for TDLS peers, make sure to return to the base channel before
* removal.
*/
if (test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) {
drv_tdls_cancel_channel_switch(local, sdata, &sta->sta);
clear_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
}

list_del_rcu(&sta->list);

drv_sta_pre_rcu_remove(local, sta->sdata, sta);
Expand Down
3 changes: 3 additions & 0 deletions net/mac80211/sta_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
* @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this
* station.
* @WLAN_STA_TDLS_CHAN_SWITCH: This TDLS peer supports TDLS channel-switching
* @WLAN_STA_TDLS_OFF_CHANNEL: The local STA is currently off-channel with this
* TDLS peer
* @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
* keeping station in power-save mode, reply when the driver
* unblocks the station.
Expand Down Expand Up @@ -80,6 +82,7 @@ enum ieee80211_sta_info_flags {
WLAN_STA_TDLS_PEER_AUTH,
WLAN_STA_TDLS_INITIATOR,
WLAN_STA_TDLS_CHAN_SWITCH,
WLAN_STA_TDLS_OFF_CHANNEL,
WLAN_STA_UAPSD,
WLAN_STA_SP,
WLAN_STA_4ADDR_EVENT,
Expand Down
Loading

0 comments on commit a7a6bdd

Please sign in to comment.