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 | |
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>
-rw-r--r-- | include/net/cfg80211.h | 14 | ||||
-rw-r--r-- | include/uapi/linux/nl80211.h | 19 | ||||
-rw-r--r-- | net/wireless/core.c | 4 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 108 | ||||
-rw-r--r-- | net/wireless/rdev-ops.h | 24 | ||||
-rw-r--r-- | net/wireless/trace.h | 42 |
6 files changed, 211 insertions, 0 deletions
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 220d5f5f1aca..8d04dfef32bf 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h | |||
@@ -2367,6 +2367,12 @@ struct cfg80211_qos_map { | |||
2367 | * (invoked with the wireless_dev mutex held) | 2367 | * (invoked with the wireless_dev mutex held) |
2368 | * @leave_ocb: leave the current OCB network | 2368 | * @leave_ocb: leave the current OCB network |
2369 | * (invoked with the wireless_dev mutex held) | 2369 | * (invoked with the wireless_dev mutex held) |
2370 | * | ||
2371 | * @tdls_channel_switch: Start channel-switching with a TDLS peer. The driver | ||
2372 | * is responsible for continually initiating channel-switching operations | ||
2373 | * and returning to the base channel for communication with the AP. | ||
2374 | * @tdls_cancel_channel_switch: Stop channel-switching with a TDLS peer. Both | ||
2375 | * peers must be on the base channel when the call completes. | ||
2370 | */ | 2376 | */ |
2371 | struct cfg80211_ops { | 2377 | struct cfg80211_ops { |
2372 | int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); | 2378 | int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); |
@@ -2622,6 +2628,14 @@ struct cfg80211_ops { | |||
2622 | u16 admitted_time); | 2628 | u16 admitted_time); |
2623 | int (*del_tx_ts)(struct wiphy *wiphy, struct net_device *dev, | 2629 | int (*del_tx_ts)(struct wiphy *wiphy, struct net_device *dev, |
2624 | u8 tsid, const u8 *peer); | 2630 | u8 tsid, const u8 *peer); |
2631 | |||
2632 | int (*tdls_channel_switch)(struct wiphy *wiphy, | ||
2633 | struct net_device *dev, | ||
2634 | const u8 *addr, u8 oper_class, | ||
2635 | struct cfg80211_chan_def *chandef); | ||
2636 | void (*tdls_cancel_channel_switch)(struct wiphy *wiphy, | ||
2637 | struct net_device *dev, | ||
2638 | const u8 *addr); | ||
2625 | }; | 2639 | }; |
2626 | 2640 | ||
2627 | /* | 2641 | /* |
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index ccdeef28d672..365db67ca71d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h | |||
@@ -762,6 +762,18 @@ | |||
762 | * @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the | 762 | * @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the |
763 | * network is determined by the network interface. | 763 | * network is determined by the network interface. |
764 | * | 764 | * |
765 | * @NL80211_CMD_TDLS_CHANNEL_SWITCH: Start channel-switching with a TDLS peer, | ||
766 | * identified by the %NL80211_ATTR_MAC parameter. A target channel is | ||
767 | * provided via %NL80211_ATTR_WIPHY_FREQ and other attributes determining | ||
768 | * channel width/type. The target operating class is given via | ||
769 | * %NL80211_ATTR_OPER_CLASS. | ||
770 | * The driver is responsible for continually initiating channel-switching | ||
771 | * operations and returning to the base channel for communication with the | ||
772 | * AP. | ||
773 | * @NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH: Stop channel-switching with a TDLS | ||
774 | * peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel | ||
775 | * when this command completes. | ||
776 | * | ||
765 | * @NL80211_CMD_MAX: highest used command number | 777 | * @NL80211_CMD_MAX: highest used command number |
766 | * @__NL80211_CMD_AFTER_LAST: internal use | 778 | * @__NL80211_CMD_AFTER_LAST: internal use |
767 | */ | 779 | */ |
@@ -943,6 +955,9 @@ enum nl80211_commands { | |||
943 | 955 | ||
944 | NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, | 956 | NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, |
945 | 957 | ||
958 | NL80211_CMD_TDLS_CHANNEL_SWITCH, | ||
959 | NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH, | ||
960 | |||
946 | /* add new commands above here */ | 961 | /* add new commands above here */ |
947 | 962 | ||
948 | /* used to define NL80211_CMD_MAX below */ | 963 | /* used to define NL80211_CMD_MAX below */ |
@@ -1669,6 +1684,8 @@ enum nl80211_commands { | |||
1669 | * @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see | 1684 | * @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see |
1670 | * &enum nl80211_smps_mode. | 1685 | * &enum nl80211_smps_mode. |
1671 | * | 1686 | * |
1687 | * @NL80211_ATTR_OPER_CLASS: operating class | ||
1688 | * | ||
1672 | * @NL80211_ATTR_MAX: highest attribute number currently defined | 1689 | * @NL80211_ATTR_MAX: highest attribute number currently defined |
1673 | * @__NL80211_ATTR_AFTER_LAST: internal use | 1690 | * @__NL80211_ATTR_AFTER_LAST: internal use |
1674 | */ | 1691 | */ |
@@ -2021,6 +2038,8 @@ enum nl80211_attrs { | |||
2021 | 2038 | ||
2022 | NL80211_ATTR_SMPS_MODE, | 2039 | NL80211_ATTR_SMPS_MODE, |
2023 | 2040 | ||
2041 | NL80211_ATTR_OPER_CLASS, | ||
2042 | |||
2024 | /* add attributes here, update the policy in nl80211.c */ | 2043 | /* add attributes here, update the policy in nl80211.c */ |
2025 | 2044 | ||
2026 | __NL80211_ATTR_AFTER_LAST, | 2045 | __NL80211_ATTR_AFTER_LAST, |
diff --git a/net/wireless/core.c b/net/wireless/core.c index a4d27927aba2..4c2e501203d1 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -541,6 +541,10 @@ int wiphy_register(struct wiphy *wiphy) | |||
541 | !wiphy->wowlan->tcp)) | 541 | !wiphy->wowlan->tcp)) |
542 | return -EINVAL; | 542 | return -EINVAL; |
543 | #endif | 543 | #endif |
544 | if (WARN_ON((wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) && | ||
545 | (!rdev->ops->tdls_channel_switch || | ||
546 | !rdev->ops->tdls_cancel_channel_switch))) | ||
547 | return -EINVAL; | ||
544 | 548 | ||
545 | if (WARN_ON(wiphy->coalesce && | 549 | if (WARN_ON(wiphy->coalesce && |
546 | (!wiphy->coalesce->n_rules || | 550 | (!wiphy->coalesce->n_rules || |
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 */ |
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 1b3864cd50ca..35cfb7134bdb 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h | |||
@@ -993,4 +993,28 @@ rdev_del_tx_ts(struct cfg80211_registered_device *rdev, | |||
993 | return ret; | 993 | return ret; |
994 | } | 994 | } |
995 | 995 | ||
996 | static inline int | ||
997 | rdev_tdls_channel_switch(struct cfg80211_registered_device *rdev, | ||
998 | struct net_device *dev, const u8 *addr, | ||
999 | u8 oper_class, struct cfg80211_chan_def *chandef) | ||
1000 | { | ||
1001 | int ret; | ||
1002 | |||
1003 | trace_rdev_tdls_channel_switch(&rdev->wiphy, dev, addr, oper_class, | ||
1004 | chandef); | ||
1005 | ret = rdev->ops->tdls_channel_switch(&rdev->wiphy, dev, addr, | ||
1006 | oper_class, chandef); | ||
1007 | trace_rdev_return_int(&rdev->wiphy, ret); | ||
1008 | return ret; | ||
1009 | } | ||
1010 | |||
1011 | static inline void | ||
1012 | rdev_tdls_cancel_channel_switch(struct cfg80211_registered_device *rdev, | ||
1013 | struct net_device *dev, const u8 *addr) | ||
1014 | { | ||
1015 | trace_rdev_tdls_cancel_channel_switch(&rdev->wiphy, dev, addr); | ||
1016 | rdev->ops->tdls_cancel_channel_switch(&rdev->wiphy, dev, addr); | ||
1017 | trace_rdev_return_void(&rdev->wiphy); | ||
1018 | } | ||
1019 | |||
996 | #endif /* __CFG80211_RDEV_OPS */ | 1020 | #endif /* __CFG80211_RDEV_OPS */ |
diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 6e25370d3ce7..ad38910f7036 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h | |||
@@ -2032,6 +2032,48 @@ TRACE_EVENT(rdev_del_tx_ts, | |||
2032 | WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->tsid) | 2032 | WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->tsid) |
2033 | ); | 2033 | ); |
2034 | 2034 | ||
2035 | TRACE_EVENT(rdev_tdls_channel_switch, | ||
2036 | TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, | ||
2037 | const u8 *addr, u8 oper_class, | ||
2038 | struct cfg80211_chan_def *chandef), | ||
2039 | TP_ARGS(wiphy, netdev, addr, oper_class, chandef), | ||
2040 | TP_STRUCT__entry( | ||
2041 | WIPHY_ENTRY | ||
2042 | NETDEV_ENTRY | ||
2043 | MAC_ENTRY(addr) | ||
2044 | __field(u8, oper_class) | ||
2045 | CHAN_DEF_ENTRY | ||
2046 | ), | ||
2047 | TP_fast_assign( | ||
2048 | WIPHY_ASSIGN; | ||
2049 | NETDEV_ASSIGN; | ||
2050 | MAC_ASSIGN(addr, addr); | ||
2051 | CHAN_DEF_ASSIGN(chandef); | ||
2052 | ), | ||
2053 | TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT | ||
2054 | " oper class %d, " CHAN_DEF_PR_FMT, | ||
2055 | WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(addr), | ||
2056 | __entry->oper_class, CHAN_DEF_PR_ARG) | ||
2057 | ); | ||
2058 | |||
2059 | TRACE_EVENT(rdev_tdls_cancel_channel_switch, | ||
2060 | TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, | ||
2061 | const u8 *addr), | ||
2062 | TP_ARGS(wiphy, netdev, addr), | ||
2063 | TP_STRUCT__entry( | ||
2064 | WIPHY_ENTRY | ||
2065 | NETDEV_ENTRY | ||
2066 | MAC_ENTRY(addr) | ||
2067 | ), | ||
2068 | TP_fast_assign( | ||
2069 | WIPHY_ASSIGN; | ||
2070 | NETDEV_ASSIGN; | ||
2071 | MAC_ASSIGN(addr, addr); | ||
2072 | ), | ||
2073 | TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT, | ||
2074 | WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(addr)) | ||
2075 | ); | ||
2076 | |||
2035 | /************************************************************* | 2077 | /************************************************************* |
2036 | * cfg80211 exported functions traces * | 2078 | * cfg80211 exported functions traces * |
2037 | *************************************************************/ | 2079 | *************************************************************/ |