diff options
author | Arik Nemtsov <arik@wizery.com> | 2014-11-19 05:54:26 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-11-19 12:45:12 -0500 |
commit | 1057d35ede5dbf7ed7842357564fb42c9b54ba50 (patch) | |
tree | ffcef6d5d942a74a1f3f71fc5aab6d910398240d /net/wireless/nl80211.c | |
parent | c2733905692589cc73928ffd65d26107536e80fe (diff) |
cfg80211: introduce TDLS channel switch commands
Introduce commands to initiate and cancel TDLS channel-switching. Once
TDLS channel-switching is started, the lower level driver is responsible
for continually initiating channel-switch operations and returning to
the base (AP) channel to listen for beacons from time to time.
Upon cancellation of the channel-switch all communication between the
relevant TDLS peers will continue on the base channel.
Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d0a8361b3395..27666f5e5050 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -9658,6 +9658,98 @@ static int nl80211_del_tx_ts(struct sk_buff *skb, struct genl_info *info) | |||
9658 | return err; | 9658 | return err; |
9659 | } | 9659 | } |
9660 | 9660 | ||
9661 | static int nl80211_tdls_channel_switch(struct sk_buff *skb, | ||
9662 | struct genl_info *info) | ||
9663 | { | ||
9664 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
9665 | struct net_device *dev = info->user_ptr[1]; | ||
9666 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
9667 | struct cfg80211_chan_def chandef = {}; | ||
9668 | const u8 *addr; | ||
9669 | u8 oper_class; | ||
9670 | int err; | ||
9671 | |||
9672 | if (!rdev->ops->tdls_channel_switch || | ||
9673 | !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH)) | ||
9674 | return -EOPNOTSUPP; | ||
9675 | |||
9676 | switch (dev->ieee80211_ptr->iftype) { | ||
9677 | case NL80211_IFTYPE_STATION: | ||
9678 | case NL80211_IFTYPE_P2P_CLIENT: | ||
9679 | break; | ||
9680 | default: | ||
9681 | return -EOPNOTSUPP; | ||
9682 | } | ||
9683 | |||
9684 | if (!info->attrs[NL80211_ATTR_MAC] || | ||
9685 | !info->attrs[NL80211_ATTR_OPER_CLASS]) | ||
9686 | return -EINVAL; | ||
9687 | |||
9688 | err = nl80211_parse_chandef(rdev, info, &chandef); | ||
9689 | if (err) | ||
9690 | return err; | ||
9691 | |||
9692 | /* | ||
9693 | * Don't allow wide channels on the 2.4Ghz band, as per IEEE802.11-2012 | ||
9694 | * section 10.22.6.2.1. Disallow 5/10Mhz channels as well for now, the | ||
9695 | * specification is not defined for them. | ||
9696 | */ | ||
9697 | if (chandef.chan->band == IEEE80211_BAND_2GHZ && | ||
9698 | chandef.width != NL80211_CHAN_WIDTH_20_NOHT && | ||
9699 | chandef.width != NL80211_CHAN_WIDTH_20) | ||
9700 | return -EINVAL; | ||
9701 | |||
9702 | /* we will be active on the TDLS link */ | ||
9703 | if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, wdev->iftype)) | ||
9704 | return -EINVAL; | ||
9705 | |||
9706 | /* don't allow switching to DFS channels */ | ||
9707 | if (cfg80211_chandef_dfs_required(wdev->wiphy, &chandef, wdev->iftype)) | ||
9708 | return -EINVAL; | ||
9709 | |||
9710 | addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
9711 | oper_class = nla_get_u8(info->attrs[NL80211_ATTR_OPER_CLASS]); | ||
9712 | |||
9713 | wdev_lock(wdev); | ||
9714 | err = rdev_tdls_channel_switch(rdev, dev, addr, oper_class, &chandef); | ||
9715 | wdev_unlock(wdev); | ||
9716 | |||
9717 | return err; | ||
9718 | } | ||
9719 | |||
9720 | static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb, | ||
9721 | struct genl_info *info) | ||
9722 | { | ||
9723 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
9724 | struct net_device *dev = info->user_ptr[1]; | ||
9725 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
9726 | const u8 *addr; | ||
9727 | |||
9728 | if (!rdev->ops->tdls_channel_switch || | ||
9729 | !rdev->ops->tdls_cancel_channel_switch || | ||
9730 | !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH)) | ||
9731 | return -EOPNOTSUPP; | ||
9732 | |||
9733 | switch (dev->ieee80211_ptr->iftype) { | ||
9734 | case NL80211_IFTYPE_STATION: | ||
9735 | case NL80211_IFTYPE_P2P_CLIENT: | ||
9736 | break; | ||
9737 | default: | ||
9738 | return -EOPNOTSUPP; | ||
9739 | } | ||
9740 | |||
9741 | if (!info->attrs[NL80211_ATTR_MAC]) | ||
9742 | return -EINVAL; | ||
9743 | |||
9744 | addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
9745 | |||
9746 | wdev_lock(wdev); | ||
9747 | rdev_tdls_cancel_channel_switch(rdev, dev, addr); | ||
9748 | wdev_unlock(wdev); | ||
9749 | |||
9750 | return 0; | ||
9751 | } | ||
9752 | |||
9661 | #define NL80211_FLAG_NEED_WIPHY 0x01 | 9753 | #define NL80211_FLAG_NEED_WIPHY 0x01 |
9662 | #define NL80211_FLAG_NEED_NETDEV 0x02 | 9754 | #define NL80211_FLAG_NEED_NETDEV 0x02 |
9663 | #define NL80211_FLAG_NEED_RTNL 0x04 | 9755 | #define NL80211_FLAG_NEED_RTNL 0x04 |
@@ -10456,6 +10548,22 @@ static const struct genl_ops nl80211_ops[] = { | |||
10456 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | 10548 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | |
10457 | NL80211_FLAG_NEED_RTNL, | 10549 | NL80211_FLAG_NEED_RTNL, |
10458 | }, | 10550 | }, |
10551 | { | ||
10552 | .cmd = NL80211_CMD_TDLS_CHANNEL_SWITCH, | ||
10553 | .doit = nl80211_tdls_channel_switch, | ||
10554 | .policy = nl80211_policy, | ||
10555 | .flags = GENL_ADMIN_PERM, | ||
10556 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | ||
10557 | NL80211_FLAG_NEED_RTNL, | ||
10558 | }, | ||
10559 | { | ||
10560 | .cmd = NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH, | ||
10561 | .doit = nl80211_tdls_cancel_channel_switch, | ||
10562 | .policy = nl80211_policy, | ||
10563 | .flags = GENL_ADMIN_PERM, | ||
10564 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | ||
10565 | NL80211_FLAG_NEED_RTNL, | ||
10566 | }, | ||
10459 | }; | 10567 | }; |
10460 | 10568 | ||
10461 | /* notification functions */ | 10569 | /* notification functions */ |