|
@@ -5634,15 +5634,26 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
|
|
static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
|
|
|
u8 radar_detect_width = 0;
|
|
|
int err;
|
|
|
+ bool need_new_beacon = false;
|
|
|
|
|
|
if (!rdev->ops->channel_switch ||
|
|
|
!(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
- /* may add IBSS support later */
|
|
|
- if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
|
|
- dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
|
|
+ switch (dev->ieee80211_ptr->iftype) {
|
|
|
+ case NL80211_IFTYPE_AP:
|
|
|
+ case NL80211_IFTYPE_P2P_GO:
|
|
|
+ need_new_beacon = true;
|
|
|
+
|
|
|
+ /* useless if AP is not running */
|
|
|
+ if (!wdev->beacon_interval)
|
|
|
+ return -EINVAL;
|
|
|
+ break;
|
|
|
+ case NL80211_IFTYPE_ADHOC:
|
|
|
+ break;
|
|
|
+ default:
|
|
|
return -EOPNOTSUPP;
|
|
|
+ }
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
@@ -5651,15 +5662,16 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
|
|
return -EINVAL;
|
|
|
|
|
|
/* only important for AP, IBSS and mesh create IEs internally */
|
|
|
- if (!info->attrs[NL80211_ATTR_CSA_IES])
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- /* useless if AP is not running */
|
|
|
- if (!wdev->beacon_interval)
|
|
|
+ if (need_new_beacon &&
|
|
|
+ (!info->attrs[NL80211_ATTR_CSA_IES] ||
|
|
|
+ !info->attrs[NL80211_ATTR_CSA_C_OFF_BEACON]))
|
|
|
return -EINVAL;
|
|
|
|
|
|
params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
|
|
|
|
|
|
+ if (!need_new_beacon)
|
|
|
+ goto skip_beacons;
|
|
|
+
|
|
|
err = nl80211_parse_beacon(info->attrs, ¶ms.beacon_after);
|
|
|
if (err)
|
|
|
return err;
|
|
@@ -5699,6 +5711,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
+skip_beacons:
|
|
|
err = nl80211_parse_chandef(rdev, info, ¶ms.chandef);
|
|
|
if (err)
|
|
|
return err;
|
|
@@ -5706,12 +5719,17 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
|
|
if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef))
|
|
|
return -EINVAL;
|
|
|
|
|
|
- err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef);
|
|
|
- if (err < 0) {
|
|
|
- return err;
|
|
|
- } else if (err) {
|
|
|
- radar_detect_width = BIT(params.chandef.width);
|
|
|
- params.radar_required = true;
|
|
|
+ /* DFS channels are only supported for AP/P2P GO ... for now. */
|
|
|
+ if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
|
|
|
+ dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) {
|
|
|
+ err = cfg80211_chandef_dfs_required(wdev->wiphy,
|
|
|
+ ¶ms.chandef);
|
|
|
+ if (err < 0) {
|
|
|
+ return err;
|
|
|
+ } else if (err) {
|
|
|
+ radar_detect_width = BIT(params.chandef.width);
|
|
|
+ params.radar_required = true;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
|
|
@@ -10740,7 +10758,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
|
|
|
wdev_lock(wdev);
|
|
|
|
|
|
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
|
|
|
- wdev->iftype != NL80211_IFTYPE_P2P_GO))
|
|
|
+ wdev->iftype != NL80211_IFTYPE_P2P_GO &&
|
|
|
+ wdev->iftype != NL80211_IFTYPE_ADHOC))
|
|
|
goto out;
|
|
|
|
|
|
wdev->channel = chandef->chan;
|