diff options
author | Simon Wunderlich <simon.wunderlich@s2003.tu-chemnitz.de> | 2013-07-11 10:09:05 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-08-01 12:30:28 -0400 |
commit | 16ef1fe272332b2f7fd99236017b891db48d9cd6 (patch) | |
tree | 95b7a750ade214349282f1858ac44640890fce9c /net/wireless | |
parent | 7cf1f14ecf1f5025abb0e30e22e8f7ad219fa32e (diff) |
nl80211/cfg80211: add channel switch command
To allow channel switch announcements within beacons, add
the channel switch command to nl80211/cfg80211. This is
implementation is intended for AP and (later) IBSS mode.
Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/nl80211.c | 122 | ||||
-rw-r--r-- | net/wireless/rdev-ops.h | 12 | ||||
-rw-r--r-- | net/wireless/trace.h | 33 |
3 files changed, 166 insertions, 1 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 03d4ef95292e..f7cb12178bd2 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -349,6 +349,11 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
349 | [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY, | 349 | [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY, |
350 | .len = IEEE80211_MAX_DATA_LEN }, | 350 | .len = IEEE80211_MAX_DATA_LEN }, |
351 | [NL80211_ATTR_PEER_AID] = { .type = NLA_U16 }, | 351 | [NL80211_ATTR_PEER_AID] = { .type = NLA_U16 }, |
352 | [NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 }, | ||
353 | [NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG }, | ||
354 | [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED }, | ||
355 | [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 }, | ||
356 | [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 }, | ||
352 | }; | 357 | }; |
353 | 358 | ||
354 | /* policy for the key attributes */ | 359 | /* policy for the key attributes */ |
@@ -1422,6 +1427,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, | |||
1422 | if (state->split) { | 1427 | if (state->split) { |
1423 | CMD(crit_proto_start, CRIT_PROTOCOL_START); | 1428 | CMD(crit_proto_start, CRIT_PROTOCOL_START); |
1424 | CMD(crit_proto_stop, CRIT_PROTOCOL_STOP); | 1429 | CMD(crit_proto_stop, CRIT_PROTOCOL_STOP); |
1430 | if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH) | ||
1431 | CMD(channel_switch, CHANNEL_SWITCH); | ||
1425 | } | 1432 | } |
1426 | 1433 | ||
1427 | #ifdef CONFIG_NL80211_TESTMODE | 1434 | #ifdef CONFIG_NL80211_TESTMODE |
@@ -5613,6 +5620,111 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, | |||
5613 | return err; | 5620 | return err; |
5614 | } | 5621 | } |
5615 | 5622 | ||
5623 | static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) | ||
5624 | { | ||
5625 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
5626 | struct net_device *dev = info->user_ptr[1]; | ||
5627 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
5628 | struct cfg80211_csa_settings params; | ||
5629 | /* csa_attrs is defined static to avoid waste of stack size - this | ||
5630 | * function is called under RTNL lock, so this should not be a problem. | ||
5631 | */ | ||
5632 | static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1]; | ||
5633 | u8 radar_detect_width = 0; | ||
5634 | int err; | ||
5635 | |||
5636 | if (!rdev->ops->channel_switch || | ||
5637 | !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)) | ||
5638 | return -EOPNOTSUPP; | ||
5639 | |||
5640 | /* may add IBSS support later */ | ||
5641 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | ||
5642 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | ||
5643 | return -EOPNOTSUPP; | ||
5644 | |||
5645 | memset(¶ms, 0, sizeof(params)); | ||
5646 | |||
5647 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || | ||
5648 | !info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]) | ||
5649 | return -EINVAL; | ||
5650 | |||
5651 | /* only important for AP, IBSS and mesh create IEs internally */ | ||
5652 | if (!info->attrs[NL80211_ATTR_CSA_IES]) | ||
5653 | return -EINVAL; | ||
5654 | |||
5655 | /* useless if AP is not running */ | ||
5656 | if (!wdev->beacon_interval) | ||
5657 | return -EINVAL; | ||
5658 | |||
5659 | params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]); | ||
5660 | |||
5661 | err = nl80211_parse_beacon(info->attrs, ¶ms.beacon_after); | ||
5662 | if (err) | ||
5663 | return err; | ||
5664 | |||
5665 | err = nla_parse_nested(csa_attrs, NL80211_ATTR_MAX, | ||
5666 | info->attrs[NL80211_ATTR_CSA_IES], | ||
5667 | nl80211_policy); | ||
5668 | if (err) | ||
5669 | return err; | ||
5670 | |||
5671 | err = nl80211_parse_beacon(csa_attrs, ¶ms.beacon_csa); | ||
5672 | if (err) | ||
5673 | return err; | ||
5674 | |||
5675 | if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]) | ||
5676 | return -EINVAL; | ||
5677 | |||
5678 | params.counter_offset_beacon = | ||
5679 | nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]); | ||
5680 | if (params.counter_offset_beacon >= params.beacon_csa.tail_len) | ||
5681 | return -EINVAL; | ||
5682 | |||
5683 | /* sanity check - counters should be the same */ | ||
5684 | if (params.beacon_csa.tail[params.counter_offset_beacon] != | ||
5685 | params.count) | ||
5686 | return -EINVAL; | ||
5687 | |||
5688 | if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) { | ||
5689 | params.counter_offset_presp = | ||
5690 | nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]); | ||
5691 | if (params.counter_offset_presp >= | ||
5692 | params.beacon_csa.probe_resp_len) | ||
5693 | return -EINVAL; | ||
5694 | |||
5695 | if (params.beacon_csa.probe_resp[params.counter_offset_presp] != | ||
5696 | params.count) | ||
5697 | return -EINVAL; | ||
5698 | } | ||
5699 | |||
5700 | err = nl80211_parse_chandef(rdev, info, ¶ms.chandef); | ||
5701 | if (err) | ||
5702 | return err; | ||
5703 | |||
5704 | if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef)) | ||
5705 | return -EINVAL; | ||
5706 | |||
5707 | err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef); | ||
5708 | if (err < 0) { | ||
5709 | return err; | ||
5710 | } else if (err) { | ||
5711 | radar_detect_width = BIT(params.chandef.width); | ||
5712 | params.radar_required = true; | ||
5713 | } | ||
5714 | |||
5715 | err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, | ||
5716 | params.chandef.chan, | ||
5717 | CHAN_MODE_SHARED, | ||
5718 | radar_detect_width); | ||
5719 | if (err) | ||
5720 | return err; | ||
5721 | |||
5722 | if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX]) | ||
5723 | params.block_tx = true; | ||
5724 | |||
5725 | return rdev_channel_switch(rdev, dev, ¶ms); | ||
5726 | } | ||
5727 | |||
5616 | static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, | 5728 | static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, |
5617 | u32 seq, int flags, | 5729 | u32 seq, int flags, |
5618 | struct cfg80211_registered_device *rdev, | 5730 | struct cfg80211_registered_device *rdev, |
@@ -9361,7 +9473,15 @@ static struct genl_ops nl80211_ops[] = { | |||
9361 | .flags = GENL_ADMIN_PERM, | 9473 | .flags = GENL_ADMIN_PERM, |
9362 | .internal_flags = NL80211_FLAG_NEED_WIPHY | | 9474 | .internal_flags = NL80211_FLAG_NEED_WIPHY | |
9363 | NL80211_FLAG_NEED_RTNL, | 9475 | NL80211_FLAG_NEED_RTNL, |
9364 | } | 9476 | }, |
9477 | { | ||
9478 | .cmd = NL80211_CMD_CHANNEL_SWITCH, | ||
9479 | .doit = nl80211_channel_switch, | ||
9480 | .policy = nl80211_policy, | ||
9481 | .flags = GENL_ADMIN_PERM, | ||
9482 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | ||
9483 | NL80211_FLAG_NEED_RTNL, | ||
9484 | }, | ||
9365 | }; | 9485 | }; |
9366 | 9486 | ||
9367 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 9487 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 9f15f0ac824d..de870d4d0bcc 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h | |||
@@ -923,4 +923,16 @@ static inline void rdev_crit_proto_stop(struct cfg80211_registered_device *rdev, | |||
923 | trace_rdev_return_void(&rdev->wiphy); | 923 | trace_rdev_return_void(&rdev->wiphy); |
924 | } | 924 | } |
925 | 925 | ||
926 | static inline int rdev_channel_switch(struct cfg80211_registered_device *rdev, | ||
927 | struct net_device *dev, | ||
928 | struct cfg80211_csa_settings *params) | ||
929 | { | ||
930 | int ret; | ||
931 | |||
932 | trace_rdev_channel_switch(&rdev->wiphy, dev, params); | ||
933 | ret = rdev->ops->channel_switch(&rdev->wiphy, dev, params); | ||
934 | trace_rdev_return_int(&rdev->wiphy, ret); | ||
935 | return ret; | ||
936 | } | ||
937 | |||
926 | #endif /* __CFG80211_RDEV_OPS */ | 938 | #endif /* __CFG80211_RDEV_OPS */ |
diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 09af6eb426a8..f0ebdcd394ef 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h | |||
@@ -1841,6 +1841,39 @@ TRACE_EVENT(rdev_crit_proto_stop, | |||
1841 | WIPHY_PR_ARG, WDEV_PR_ARG) | 1841 | WIPHY_PR_ARG, WDEV_PR_ARG) |
1842 | ); | 1842 | ); |
1843 | 1843 | ||
1844 | TRACE_EVENT(rdev_channel_switch, | ||
1845 | TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, | ||
1846 | struct cfg80211_csa_settings *params), | ||
1847 | TP_ARGS(wiphy, netdev, params), | ||
1848 | TP_STRUCT__entry( | ||
1849 | WIPHY_ENTRY | ||
1850 | NETDEV_ENTRY | ||
1851 | CHAN_DEF_ENTRY | ||
1852 | __field(u16, counter_offset_beacon) | ||
1853 | __field(u16, counter_offset_presp) | ||
1854 | __field(bool, radar_required) | ||
1855 | __field(bool, block_tx) | ||
1856 | __field(u8, count) | ||
1857 | ), | ||
1858 | TP_fast_assign( | ||
1859 | WIPHY_ASSIGN; | ||
1860 | NETDEV_ASSIGN; | ||
1861 | CHAN_DEF_ASSIGN(¶ms->chandef); | ||
1862 | __entry->counter_offset_beacon = params->counter_offset_beacon; | ||
1863 | __entry->counter_offset_presp = params->counter_offset_presp; | ||
1864 | __entry->radar_required = params->radar_required; | ||
1865 | __entry->block_tx = params->block_tx; | ||
1866 | __entry->count = params->count; | ||
1867 | ), | ||
1868 | TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT | ||
1869 | ", block_tx: %d, count: %u, radar_required: %d" | ||
1870 | ", counter offsets (beacon/presp): %u/%u", | ||
1871 | WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG, | ||
1872 | __entry->block_tx, __entry->count, __entry->radar_required, | ||
1873 | __entry->counter_offset_beacon, | ||
1874 | __entry->counter_offset_presp) | ||
1875 | ); | ||
1876 | |||
1844 | /************************************************************* | 1877 | /************************************************************* |
1845 | * cfg80211 exported functions traces * | 1878 | * cfg80211 exported functions traces * |
1846 | *************************************************************/ | 1879 | *************************************************************/ |