aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArik Nemtsov <arik@wizery.com>2014-11-19 05:54:26 -0500
committerJohannes Berg <johannes.berg@intel.com>2014-11-19 12:45:12 -0500
commit1057d35ede5dbf7ed7842357564fb42c9b54ba50 (patch)
treeffcef6d5d942a74a1f3f71fc5aab6d910398240d
parentc2733905692589cc73928ffd65d26107536e80fe (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.h14
-rw-r--r--include/uapi/linux/nl80211.h19
-rw-r--r--net/wireless/core.c4
-rw-r--r--net/wireless/nl80211.c108
-rw-r--r--net/wireless/rdev-ops.h24
-rw-r--r--net/wireless/trace.h42
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 */
2371struct cfg80211_ops { 2377struct 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
9661static 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
9720static 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
996static inline int
997rdev_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
1011static inline void
1012rdev_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
2035TRACE_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
2059TRACE_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 *************************************************************/